mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-09-06 11:34:57 +02:00
commit
524293ea79
269 changed files with 4759 additions and 3078 deletions
|
@ -1,22 +1,19 @@
|
||||||
using MediaBrowser.Controller.Devices;
|
using MediaBrowser.Controller.Devices;
|
||||||
using MediaBrowser.Controller.Net;
|
using MediaBrowser.Controller.Net;
|
||||||
using MediaBrowser.Model.Devices;
|
using MediaBrowser.Model.Devices;
|
||||||
|
using MediaBrowser.Model.Querying;
|
||||||
using MediaBrowser.Model.Session;
|
using MediaBrowser.Model.Session;
|
||||||
using ServiceStack;
|
using ServiceStack;
|
||||||
using ServiceStack.Web;
|
using ServiceStack.Web;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Devices
|
namespace MediaBrowser.Api.Devices
|
||||||
{
|
{
|
||||||
[Route("/Devices", "GET", Summary = "Gets all devices")]
|
[Route("/Devices", "GET", Summary = "Gets all devices")]
|
||||||
[Authenticated(Roles = "Admin")]
|
[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")]
|
[Route("/Devices", "DELETE", Summary = "Deletes a device")]
|
||||||
|
@ -109,16 +106,7 @@ namespace MediaBrowser.Api.Devices
|
||||||
|
|
||||||
public object Get(GetDevices request)
|
public object Get(GetDevices request)
|
||||||
{
|
{
|
||||||
var devices = _deviceManager.GetDevices();
|
return ToOptimizedResult(_deviceManager.GetDevices(request));
|
||||||
|
|
||||||
if (request.SupportsContentUploading.HasValue)
|
|
||||||
{
|
|
||||||
var val = request.SupportsContentUploading.Value;
|
|
||||||
|
|
||||||
devices = devices.Where(i => _deviceManager.GetCapabilities(i.Id).SupportsContentUploading == val);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ToOptimizedResult(devices.ToList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Get(GetCameraUploads request)
|
public object Get(GetCameraUploads request)
|
||||||
|
|
49
MediaBrowser.Api/IHasDtoOptions.cs
Normal file
49
MediaBrowser.Api/IHasDtoOptions.cs
Normal file
|
@ -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,
|
private async Task<object> GetImageResult(IHasImages item,
|
||||||
ImageRequest request,
|
ImageRequest request,
|
||||||
ItemImageInfo image,
|
ItemImageInfo image,
|
||||||
ImageOutputFormat format,
|
ImageFormat format,
|
||||||
List<IImageEnhancer> enhancers,
|
List<IImageEnhancer> enhancers,
|
||||||
string contentType,
|
string contentType,
|
||||||
TimeSpan? cacheDuration,
|
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))
|
if (!string.IsNullOrWhiteSpace(request.Format))
|
||||||
{
|
{
|
||||||
ImageOutputFormat format;
|
ImageFormat format;
|
||||||
if (Enum.TryParse(request.Format, true, out format))
|
if (Enum.TryParse(request.Format, true, out format))
|
||||||
{
|
{
|
||||||
return format;
|
return format;
|
||||||
|
@ -627,28 +627,28 @@ namespace MediaBrowser.Api.Images
|
||||||
|
|
||||||
var clientFormats = GetClientSupportedFormats();
|
var clientFormats = GetClientSupportedFormats();
|
||||||
|
|
||||||
if (serverFormats.Contains(ImageOutputFormat.Webp) &&
|
if (serverFormats.Contains(ImageFormat.Webp) &&
|
||||||
clientFormats.Contains(ImageOutputFormat.Webp))
|
clientFormats.Contains(ImageFormat.Webp))
|
||||||
{
|
{
|
||||||
return ImageOutputFormat.Webp;
|
return ImageFormat.Webp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enhancers.Count > 0)
|
if (enhancers.Count > 0)
|
||||||
{
|
{
|
||||||
return ImageOutputFormat.Png;
|
return ImageFormat.Png;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.Equals(Path.GetExtension(image.Path), ".jpg", StringComparison.OrdinalIgnoreCase) ||
|
if (string.Equals(Path.GetExtension(image.Path), ".jpg", StringComparison.OrdinalIgnoreCase) ||
|
||||||
string.Equals(Path.GetExtension(image.Path), ".jpeg", 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
|
// 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))
|
if ((Request.AcceptTypes ?? new string[] { }).Contains("image/webp", StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
@ -657,32 +657,32 @@ namespace MediaBrowser.Api.Images
|
||||||
// Not displaying properly on iOS
|
// Not displaying properly on iOS
|
||||||
if (userAgent.IndexOf("cfnetwork", StringComparison.OrdinalIgnoreCase) == -1)
|
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");
|
return Common.Net.MimeTypes.GetMimeType("i.bmp");
|
||||||
}
|
}
|
||||||
if (format == ImageOutputFormat.Gif)
|
if (format == ImageFormat.Gif)
|
||||||
{
|
{
|
||||||
return Common.Net.MimeTypes.GetMimeType("i.gif");
|
return Common.Net.MimeTypes.GetMimeType("i.gif");
|
||||||
}
|
}
|
||||||
if (format == ImageOutputFormat.Jpg)
|
if (format == ImageFormat.Jpg)
|
||||||
{
|
{
|
||||||
return Common.Net.MimeTypes.GetMimeType("i.jpg");
|
return Common.Net.MimeTypes.GetMimeType("i.jpg");
|
||||||
}
|
}
|
||||||
if (format == ImageOutputFormat.Png)
|
if (format == ImageFormat.Png)
|
||||||
{
|
{
|
||||||
return Common.Net.MimeTypes.GetMimeType("i.png");
|
return Common.Net.MimeTypes.GetMimeType("i.png");
|
||||||
}
|
}
|
||||||
if (format == ImageOutputFormat.Webp)
|
if (format == ImageFormat.Webp)
|
||||||
{
|
{
|
||||||
return Common.Net.MimeTypes.GetMimeType("i.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")]
|
[Route("/Items/RemoteSearch/AdultVideo", "POST")]
|
||||||
[Authenticated]
|
[Authenticated]
|
||||||
public class GetAdultVideoRemoteSearchResults : RemoteSearchQuery<ItemLookupInfo>, IReturn<List<RemoteSearchResult>>
|
public class GetAdultVideoRemoteSearchResults : RemoteSearchQuery<ItemLookupInfo>, IReturn<List<RemoteSearchResult>>
|
||||||
|
@ -162,13 +156,6 @@ namespace MediaBrowser.Api
|
||||||
return ToOptimizedResult(result);
|
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)
|
public object Post(GetMusicAlbumRemoteSearchResults request)
|
||||||
{
|
{
|
||||||
var result = _providerManager.GetRemoteSearchResults<MusicAlbum, AlbumInfo>(request, CancellationToken.None).Result;
|
var result = _providerManager.GetRemoteSearchResults<MusicAlbum, AlbumInfo>(request, CancellationToken.None).Result;
|
||||||
|
|
|
@ -77,6 +77,7 @@
|
||||||
<Compile Include="Dlna\DlnaServerService.cs" />
|
<Compile Include="Dlna\DlnaServerService.cs" />
|
||||||
<Compile Include="Dlna\DlnaService.cs" />
|
<Compile Include="Dlna\DlnaService.cs" />
|
||||||
<Compile Include="FilterService.cs" />
|
<Compile Include="FilterService.cs" />
|
||||||
|
<Compile Include="IHasDtoOptions.cs" />
|
||||||
<Compile Include="Library\ChapterService.cs" />
|
<Compile Include="Library\ChapterService.cs" />
|
||||||
<Compile Include="Playback\Hls\MpegDashService.cs" />
|
<Compile Include="Playback\Hls\MpegDashService.cs" />
|
||||||
<Compile Include="Playback\MediaInfoService.cs" />
|
<Compile Include="Playback\MediaInfoService.cs" />
|
||||||
|
|
|
@ -200,6 +200,19 @@ namespace MediaBrowser.Api.Movies
|
||||||
.ToList();
|
.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();
|
var items = SimilarItemsHelper.GetSimilaritems(item, list, getSimilarityScore).ToList();
|
||||||
|
|
||||||
IEnumerable<BaseItem> returnItems = items;
|
IEnumerable<BaseItem> returnItems = items;
|
||||||
|
|
|
@ -92,7 +92,7 @@ namespace MediaBrowser.Api.Movies
|
||||||
Logger,
|
Logger,
|
||||||
|
|
||||||
// Strip out secondary versions
|
// 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);
|
SimilarItemsHelper.GetSimiliarityScore);
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Net;
|
using MediaBrowser.Controller.Net;
|
||||||
|
using MediaBrowser.Controller.Playlists;
|
||||||
using MediaBrowser.Model.Querying;
|
using MediaBrowser.Model.Querying;
|
||||||
using ServiceStack;
|
using ServiceStack;
|
||||||
using System.Collections.Generic;
|
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")]
|
[Route("/Artists/{Name}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given artist")]
|
||||||
public class GetInstantMixFromArtist : BaseGetSimilarItems
|
public class GetInstantMixFromArtist : BaseGetSimilarItems
|
||||||
{
|
{
|
||||||
|
@ -109,6 +115,17 @@ namespace MediaBrowser.Api.Music
|
||||||
return GetResult(items, user, request);
|
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)
|
public object Get(GetInstantMixFromMusicGenre request)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(request.UserId.Value);
|
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.Controller.Net;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
|
@ -103,6 +102,7 @@ namespace MediaBrowser.Api
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClient _httpClient;
|
||||||
private readonly INetworkManager _netManager;
|
private readonly INetworkManager _netManager;
|
||||||
private readonly IJsonSerializer _serializer;
|
private readonly IJsonSerializer _serializer;
|
||||||
|
private const string MbAdminUrl = "https://www.mb3admin.com/admin/";
|
||||||
|
|
||||||
public PackageReviewService(IHttpClient client, INetworkManager net, IJsonSerializer serializer)
|
public PackageReviewService(IHttpClient client, INetworkManager net, IJsonSerializer serializer)
|
||||||
{
|
{
|
||||||
|
@ -132,7 +132,7 @@ namespace MediaBrowser.Api
|
||||||
parms += "&title=true";
|
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);
|
var reviews = _serializer.DeserializeFromStream<List<PackageReviewInfo>>(result);
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ namespace MediaBrowser.Api
|
||||||
{ "review", reviewText },
|
{ "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)
|
public object Get(GetPackage request)
|
||||||
{
|
{
|
||||||
var packages = _installationManager.GetAvailablePackages(CancellationToken.None, applicationVersion: _appHost.ApplicationVersion).Result;
|
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))
|
var result = list.FirstOrDefault(p => string.Equals(p.guid, request.AssemblyGuid ?? "none", StringComparison.OrdinalIgnoreCase))
|
||||||
?? packages.FirstOrDefault(p => p.name.Equals(request.Name, StringComparison.OrdinalIgnoreCase));
|
?? list.FirstOrDefault(p => p.name.Equals(request.Name, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
return ToOptimizedResult(result);
|
return ToOptimizedResult(result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2011,11 +2011,6 @@ namespace MediaBrowser.Api.Playback
|
||||||
state.EstimateContentLength = transcodingProfile.EstimateContentLength;
|
state.EstimateContentLength = transcodingProfile.EstimateContentLength;
|
||||||
state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
|
state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
|
||||||
state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
|
state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
|
||||||
|
|
||||||
if (state.VideoRequest != null && string.IsNullOrWhiteSpace(state.VideoRequest.Profile))
|
|
||||||
{
|
|
||||||
state.VideoRequest.Profile = transcodingProfile.VideoProfile;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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")]
|
[ApiMember(Name = "SupportsContentUploading", Description = "Determines whether camera upload is supported.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
|
||||||
public bool SupportsContentUploading { get; set; }
|
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")]
|
[Route("/Sessions/Logout", "POST", Summary = "Reports that a session has ended")]
|
||||||
|
@ -516,7 +522,11 @@ namespace MediaBrowser.Api.Session
|
||||||
|
|
||||||
MessageCallbackUrl = request.MessageCallbackUrl,
|
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)
|
public void Post(ReportStartupWizardComplete request)
|
||||||
{
|
{
|
||||||
_config.Configuration.IsStartupWizardCompleted = true;
|
_config.Configuration.IsStartupWizardCompleted = true;
|
||||||
|
_config.Configuration.EnableLocalizedGuids = true;
|
||||||
_config.SaveConfiguration();
|
_config.SaveConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace MediaBrowser.Api
|
||||||
/// Class GetNextUpEpisodes
|
/// Class GetNextUpEpisodes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Route("/Shows/NextUp", "GET", Summary = "Gets a list of next up episodes")]
|
[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>
|
/// <summary>
|
||||||
/// Gets or sets the user id.
|
/// Gets or sets the user id.
|
||||||
|
@ -58,10 +58,19 @@ namespace MediaBrowser.Api
|
||||||
/// <value>The parent id.</value>
|
/// <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")]
|
[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; }
|
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")]
|
[Route("/Shows/Upcoming", "GET", Summary = "Gets a list of upcoming episodes")]
|
||||||
public class GetUpcomingEpisodes : IReturn<ItemsResult>, IHasItemFields
|
public class GetUpcomingEpisodes : IReturn<ItemsResult>, IHasDtoOptions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the user id.
|
/// Gets or sets the user id.
|
||||||
|
@ -97,6 +106,15 @@ namespace MediaBrowser.Api
|
||||||
/// <value>The parent id.</value>
|
/// <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")]
|
[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; }
|
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.")]
|
[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 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
|
var result = new ItemsResult
|
||||||
{
|
{
|
||||||
|
@ -283,9 +301,9 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
var user = _userManager.GetUserById(request.UserId);
|
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
|
return ToOptimizedSerializedResultUsingCache(new ItemsResult
|
||||||
{
|
{
|
||||||
|
|
|
@ -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 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();
|
result.Items = dtos.Where(i => i != null).ToArray();
|
||||||
|
|
||||||
|
@ -332,12 +332,12 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">The item.</param>
|
/// <param name="item">The item.</param>
|
||||||
/// <param name="user">The user.</param>
|
/// <param name="user">The user.</param>
|
||||||
/// <param name="fields">The fields.</param>
|
/// <param name="options">The options.</param>
|
||||||
/// <param name="libraryItems">The library items.</param>
|
/// <param name="libraryItems">The library items.</param>
|
||||||
/// <returns>Task{DtoBaseItem}.</returns>
|
/// <returns>Task{DtoBaseItem}.</returns>
|
||||||
private BaseItemDto GetDto(TItemType item, User user, List<ItemFields> fields, 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;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,13 @@ using System.Linq;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.UserLibrary
|
namespace MediaBrowser.Api.UserLibrary
|
||||||
{
|
{
|
||||||
public abstract class BaseItemsRequest : IHasItemFields
|
public abstract class BaseItemsRequest : IHasDtoOptions
|
||||||
{
|
{
|
||||||
|
protected BaseItemsRequest()
|
||||||
|
{
|
||||||
|
EnableImages = true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Skips over a given number of items within the results. Use for paging.
|
/// Skips over a given number of items within the results. Use for paging.
|
||||||
/// </summary>
|
/// </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)]
|
[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; }
|
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()
|
public string[] GetGenres()
|
||||||
{
|
{
|
||||||
return (Genres ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
|
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 result = await GetItemsToSerialize(request, user, parentItem).ConfigureAwait(false);
|
||||||
|
|
||||||
var isFiltered = result.Item2;
|
var isFiltered = result.Item2;
|
||||||
|
var dtoOptions = request.GetDtoOptions();
|
||||||
|
|
||||||
if (isFiltered)
|
if (isFiltered)
|
||||||
{
|
{
|
||||||
var currentFields = request.GetItemFields().ToList();
|
|
||||||
|
|
||||||
return new ItemsResult
|
return new ItemsResult
|
||||||
{
|
{
|
||||||
TotalRecordCount = result.Item1.TotalRecordCount,
|
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 pagedItems = ApplyPaging(request, itemsArray);
|
||||||
|
|
||||||
var fields = request.GetItemFields().ToList();
|
var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray();
|
||||||
|
|
||||||
var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray();
|
|
||||||
|
|
||||||
return new ItemsResult
|
return new ItemsResult
|
||||||
{
|
{
|
||||||
|
@ -880,7 +877,7 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
var hasTrailers = i as IHasTrailers;
|
var hasTrailers = i as IHasTrailers;
|
||||||
if (hasTrailers != null)
|
if (hasTrailers != null)
|
||||||
{
|
{
|
||||||
trailerCount = hasTrailers.LocalTrailerIds.Count;
|
trailerCount = hasTrailers.GetTrailerIds().Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ok = val ? trailerCount > 0 : trailerCount == 0;
|
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")]
|
[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>
|
/// <summary>
|
||||||
/// Gets or sets the user id.
|
/// 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")]
|
[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; }
|
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()
|
public GetLatestMedia()
|
||||||
{
|
{
|
||||||
Limit = 20;
|
Limit = 20;
|
||||||
|
@ -362,7 +371,7 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var fields = request.GetItemFields().ToList();
|
var options = request.GetDtoOptions();
|
||||||
|
|
||||||
var dtos = list.Select(i =>
|
var dtos = list.Select(i =>
|
||||||
{
|
{
|
||||||
|
@ -375,7 +384,7 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
childCount = i.Item2.Count;
|
childCount = i.Item2.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
var dto = _dtoService.GetBaseItemDto(item, fields, user);
|
var dto = _dtoService.GetBaseItemDto(item, options, user);
|
||||||
|
|
||||||
dto.ChildCount = childCount;
|
dto.ChildCount = childCount;
|
||||||
|
|
||||||
|
@ -506,7 +515,7 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
var hasTrailers = item as IHasTrailers;
|
var hasTrailers = item as IHasTrailers;
|
||||||
if (hasTrailers != null)
|
if (hasTrailers != null)
|
||||||
{
|
{
|
||||||
trailerIds = hasTrailers.LocalTrailerIds;
|
trailerIds = hasTrailers.GetTrailerIds();
|
||||||
}
|
}
|
||||||
|
|
||||||
var dtos = trailerIds
|
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.Extensions;
|
||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
|
@ -134,9 +135,22 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||||
request.Referer = options.Referer;
|
request.Referer = options.Referer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//request.ServicePoint.BindIPEndPointDelegate = BindIPEndPointCallback;
|
||||||
|
|
||||||
return request;
|
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)
|
private void AddRequestHeaders(HttpWebRequest request, HttpRequestOptions options)
|
||||||
{
|
{
|
||||||
foreach (var header in options.RequestHeaders.ToList())
|
foreach (var header in options.RequestHeaders.ToList())
|
||||||
|
|
|
@ -15,6 +15,11 @@ namespace MediaBrowser.Common.Implementations.Logging
|
||||||
/// <returns>StringBuilder.</returns>
|
/// <returns>StringBuilder.</returns>
|
||||||
public static StringBuilder GetLogMessage(Exception exception)
|
public static StringBuilder GetLogMessage(Exception exception)
|
||||||
{
|
{
|
||||||
|
if (exception == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("exception");
|
||||||
|
}
|
||||||
|
|
||||||
var messageText = new StringBuilder();
|
var messageText = new StringBuilder();
|
||||||
|
|
||||||
messageText.AppendLine(exception.Message);
|
messageText.AppendLine(exception.Message);
|
||||||
|
|
|
@ -24,14 +24,28 @@ namespace MediaBrowser.Common.Implementations.Networking
|
||||||
/// <returns>IPAddress.</returns>
|
/// <returns>IPAddress.</returns>
|
||||||
public IEnumerable<string> GetLocalIpAddresses()
|
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)
|
if (list.Count > 0)
|
||||||
{
|
{
|
||||||
return list;
|
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)
|
private bool IsInPrivateAddressSpace(string endpoint)
|
||||||
|
|
|
@ -17,7 +17,9 @@ namespace MediaBrowser.Common.Implementations.Security
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PluginSecurityManager : ISecurityManager
|
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>
|
/// <summary>
|
||||||
/// The _is MB supporter
|
/// The _is MB supporter
|
||||||
|
@ -160,7 +162,7 @@ namespace MediaBrowser.Common.Implementations.Security
|
||||||
return new SupporterInfo();
|
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))
|
using (var stream = await _httpClient.Get(url, CancellationToken.None).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
|
|
|
@ -161,7 +161,7 @@ namespace MediaBrowser.Common.Implementations.Updates
|
||||||
{ "systemid", _applicationHost.SystemId }
|
{ "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();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
@ -172,6 +172,7 @@ namespace MediaBrowser.Common.Implementations.Updates
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tuple<List<PackageInfo>, DateTime> _lastPackageListResult;
|
private Tuple<List<PackageInfo>, DateTime> _lastPackageListResult;
|
||||||
|
private const string MbAdminUrl = "https://www.mb3admin.com/admin/";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all available packages.
|
/// 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();
|
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;
|
||||||
using System.Globalization;
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
@ -55,28 +54,6 @@ namespace MediaBrowser.Common.Extensions
|
||||||
return sb.ToString();
|
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>
|
/// <summary>
|
||||||
/// Gets the M d5.
|
/// Gets the M d5.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -96,6 +73,8 @@ namespace MediaBrowser.Common.Extensions
|
||||||
/// <param name="str">The STR.</param>
|
/// <param name="str">The STR.</param>
|
||||||
/// <param name="type">The type.</param>
|
/// <param name="type">The type.</param>
|
||||||
/// <returns>Guid.</returns>
|
/// <returns>Guid.</returns>
|
||||||
|
/// <exception cref="System.ArgumentNullException">type</exception>
|
||||||
|
[Obsolete("Use LibraryManager.GetNewItemId")]
|
||||||
public static Guid GetMBId(this string str, Type type)
|
public static Guid GetMBId(this string str, Type type)
|
||||||
{
|
{
|
||||||
if (type == null)
|
if (type == null)
|
||||||
|
@ -107,35 +86,5 @@ namespace MediaBrowser.Common.Extensions
|
||||||
|
|
||||||
return key.GetMD5();
|
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\ConfigurationUpdateEventArgs.cs" />
|
||||||
<Compile Include="Configuration\IConfigurationManager.cs" />
|
<Compile Include="Configuration\IConfigurationManager.cs" />
|
||||||
<Compile Include="Configuration\IConfigurationFactory.cs" />
|
<Compile Include="Configuration\IConfigurationFactory.cs" />
|
||||||
<Compile Include="Constants\Constants.cs" />
|
|
||||||
<Compile Include="Events\EventHelper.cs" />
|
<Compile Include="Events\EventHelper.cs" />
|
||||||
<Compile Include="Extensions\BaseExtensions.cs" />
|
<Compile Include="Extensions\BaseExtensions.cs" />
|
||||||
<Compile Include="Extensions\ResourceNotFoundException.cs" />
|
<Compile Include="Extensions\ResourceNotFoundException.cs" />
|
||||||
<Compile Include="IDependencyContainer.cs" />
|
<Compile Include="IDependencyContainer.cs" />
|
||||||
<Compile Include="IO\FileSystemRepository.cs" />
|
|
||||||
<Compile Include="IO\IFileSystem.cs" />
|
<Compile Include="IO\IFileSystem.cs" />
|
||||||
<Compile Include="IO\ProgressStream.cs" />
|
<Compile Include="IO\ProgressStream.cs" />
|
||||||
<Compile Include="IO\StreamDefaults.cs" />
|
<Compile Include="IO\StreamDefaults.cs" />
|
||||||
|
|
|
@ -15,11 +15,13 @@ namespace MediaBrowser.Controller.Collections
|
||||||
public Dictionary<string, string> ProviderIds { get; set; }
|
public Dictionary<string, string> ProviderIds { get; set; }
|
||||||
|
|
||||||
public List<Guid> ItemIdList { get; set; }
|
public List<Guid> ItemIdList { get; set; }
|
||||||
|
public List<Guid> UserIds { get; set; }
|
||||||
|
|
||||||
public CollectionCreationOptions()
|
public CollectionCreationOptions()
|
||||||
{
|
{
|
||||||
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
ItemIdList = new List<Guid>();
|
ItemIdList = new List<Guid>();
|
||||||
|
UserIds = new List<Guid>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using MediaBrowser.Model.Devices;
|
using MediaBrowser.Model.Devices;
|
||||||
using MediaBrowser.Model.Events;
|
using MediaBrowser.Model.Events;
|
||||||
|
using MediaBrowser.Model.Querying;
|
||||||
using MediaBrowser.Model.Session;
|
using MediaBrowser.Model.Session;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -58,8 +59,9 @@ namespace MediaBrowser.Controller.Devices
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the devices.
|
/// Gets the devices.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="query">The query.</param>
|
||||||
/// <returns>IEnumerable<DeviceInfo>.</returns>
|
/// <returns>IEnumerable<DeviceInfo>.</returns>
|
||||||
IEnumerable<DeviceInfo> GetDevices();
|
QueryResult<DeviceInfo> GetDevices(DeviceQuery query);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes the device.
|
/// 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;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Dlna
|
namespace MediaBrowser.Controller.Dlna
|
||||||
|
@ -69,6 +70,6 @@ namespace MediaBrowser.Controller.Dlna
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filename">The filename.</param>
|
/// <param name="filename">The filename.</param>
|
||||||
/// <returns>DlnaIconResponse.</returns>
|
/// <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.
|
/// Gets the supported image output formats.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>ImageOutputFormat[].</returns>
|
/// <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 List<IImageEnhancer> Enhancers { get; set; }
|
||||||
|
|
||||||
public ImageOutputFormat OutputFormat { get; set; }
|
public ImageFormat OutputFormat { get; set; }
|
||||||
|
|
||||||
public bool AddPlayedIndicator { get; set; }
|
public bool AddPlayedIndicator { get; set; }
|
||||||
|
|
||||||
|
|
28
MediaBrowser.Controller/Drawing/ImageStream.cs
Normal file
28
MediaBrowser.Controller/Drawing/ImageStream.cs
Normal file
|
@ -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>
|
/// </summary>
|
||||||
/// <param name="dto">The dto.</param>
|
/// <param name="dto">The dto.</param>
|
||||||
/// <param name="item">The item.</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>
|
/// <summary>
|
||||||
/// Gets the base item dto.
|
/// Gets the base item dto.
|
||||||
|
@ -34,6 +35,16 @@ namespace MediaBrowser.Controller.Dto
|
||||||
/// <returns>Task{BaseItemDto}.</returns>
|
/// <returns>Task{BaseItemDto}.</returns>
|
||||||
BaseItemDto GetBaseItemDto(BaseItem item, List<ItemFields> fields, User user = null, BaseItem owner = null);
|
BaseItemDto GetBaseItemDto(BaseItem item, List<ItemFields> fields, User user = null, BaseItem owner = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the 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>
|
/// <summary>
|
||||||
/// Gets the chapter information dto.
|
/// Gets the chapter information dto.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -51,12 +62,13 @@ namespace MediaBrowser.Controller.Dto
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the item by name dto.
|
/// Gets the item by name dto.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
/// <param name="item">The item.</param>
|
/// <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="taggedItems">The tagged items.</param>
|
||||||
/// <param name="user">The user.</param>
|
/// <param name="user">The user.</param>
|
||||||
/// <returns>BaseItemDto.</returns>
|
/// <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;
|
where T : BaseItem, IItemByName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,16 +53,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
public static string ThemeSongFilename = "theme";
|
public static string ThemeSongFilename = "theme";
|
||||||
public static string ThemeVideosFolderName = "backdrops";
|
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; }
|
public List<ItemImageInfo> ImageInfos { get; set; }
|
||||||
|
|
||||||
[IgnoreDataMember]
|
[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>
|
/// <summary>
|
||||||
/// This is just a helper for convenience
|
/// This is just a helper for convenience
|
||||||
/// </summary>
|
/// </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()
|
public string GetInternalMetadataPath()
|
||||||
{
|
{
|
||||||
return GetInternalMetadataPath(ConfigurationManager.ApplicationPaths.InternalMetadataPath);
|
return GetInternalMetadataPath(ConfigurationManager.ApplicationPaths.InternalMetadataPath);
|
||||||
|
@ -593,118 +606,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return PlayAccess.Full;
|
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>
|
/// <summary>
|
||||||
/// Loads the theme songs.
|
/// Loads the theme songs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -721,7 +622,9 @@ namespace MediaBrowser.Controller.Entities
|
||||||
.Where(i => string.Equals(FileSystem.GetFileNameWithoutExtension(i), ThemeSongFilename, StringComparison.OrdinalIgnoreCase))
|
.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
|
// 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;
|
var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio;
|
||||||
|
@ -731,10 +634,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
audio = dbItem;
|
audio = dbItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio != null)
|
|
||||||
{
|
|
||||||
audio.ExtraType = ExtraType.ThemeSong;
|
audio.ExtraType = ExtraType.ThemeSong;
|
||||||
}
|
|
||||||
|
|
||||||
return audio;
|
return audio;
|
||||||
|
|
||||||
|
@ -752,7 +652,9 @@ namespace MediaBrowser.Controller.Entities
|
||||||
.Where(i => string.Equals(i.Name, ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
|
.Where(i => string.Equals(i.Name, ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
|
||||||
.SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly));
|
.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
|
// 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;
|
var dbItem = LibraryManager.GetItemById(item.Id) as Video;
|
||||||
|
@ -762,10 +664,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
item = dbItem;
|
item = dbItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item != null)
|
|
||||||
{
|
|
||||||
item.ExtraType = ExtraType.ThemeVideo;
|
item.ExtraType = ExtraType.ThemeVideo;
|
||||||
}
|
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
|
|
||||||
|
@ -870,7 +769,8 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
private async Task<bool> RefreshLocalTrailers(IHasTrailers item, MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
|
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 newItemIds = newItems.Select(i => i.Id).ToList();
|
||||||
|
|
||||||
var itemsChanged = !item.LocalTrailerIds.SequenceEqual(newItemIds);
|
var itemsChanged = !item.LocalTrailerIds.SequenceEqual(newItemIds);
|
||||||
|
@ -995,6 +895,28 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return Id.ToString();
|
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>
|
/// <summary>
|
||||||
/// Gets the preferred metadata language.
|
/// Gets the preferred metadata language.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1778,7 +1700,8 @@ namespace MediaBrowser.Controller.Entities
|
||||||
Name = Name,
|
Name = Name,
|
||||||
ProviderIds = ProviderIds,
|
ProviderIds = ProviderIds,
|
||||||
IndexNumber = IndexNumber,
|
IndexNumber = IndexNumber,
|
||||||
ParentIndexNumber = ParentIndexNumber
|
ParentIndexNumber = ParentIndexNumber,
|
||||||
|
Year = ProductionYear
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1820,9 +1743,42 @@ namespace MediaBrowser.Controller.Entities
|
||||||
if (pct > 0)
|
if (pct > 0)
|
||||||
{
|
{
|
||||||
pct = userData.PlaybackPositionTicks / pct;
|
pct = userData.PlaybackPositionTicks / pct;
|
||||||
|
|
||||||
|
if (pct > 0)
|
||||||
|
{
|
||||||
dto.PlayedPercentage = 100 * pct;
|
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.Entities.TV;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Localization;
|
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>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether this instance is physical root.
|
/// Gets or sets a value indicating whether this instance is physical root.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -103,7 +116,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
if (item.Id == Guid.Empty)
|
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))
|
if (ActualChildren.Any(i => i.Id == item.Id))
|
||||||
|
@ -364,47 +377,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
private bool IsValidFromResolver(BaseItem current, BaseItem newItem)
|
private bool IsValidFromResolver(BaseItem current, BaseItem newItem)
|
||||||
{
|
{
|
||||||
var currentAsVideo = current as Video;
|
return current.IsValidFromResolver(newItem);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -737,7 +710,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
var collectionType = LibraryManager.FindCollectionType(this);
|
var collectionType = LibraryManager.FindCollectionType(this);
|
||||||
|
|
||||||
return LibraryManager.ResolvePaths<BaseItem>(GetFileSystemChildren(directoryService), directoryService, this, collectionType);
|
return LibraryManager.ResolvePaths(GetFileSystemChildren(directoryService), directoryService, this, collectionType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -782,6 +755,12 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
private BaseItem RetrieveChild(BaseItem child)
|
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);
|
var item = LibraryManager.GetMemoryItemById(child.Id);
|
||||||
|
|
||||||
if (item != null)
|
if (item != null)
|
||||||
|
|
|
@ -28,12 +28,14 @@ namespace MediaBrowser.Controller.Entities
|
||||||
SoundtrackIds = new List<Guid>();
|
SoundtrackIds = new List<Guid>();
|
||||||
RemoteTrailers = new List<MediaUrl>();
|
RemoteTrailers = new List<MediaUrl>();
|
||||||
LocalTrailerIds = new List<Guid>();
|
LocalTrailerIds = new List<Guid>();
|
||||||
|
RemoteTrailerIds = new List<Guid>();
|
||||||
ThemeSongIds = new List<Guid>();
|
ThemeSongIds = new List<Guid>();
|
||||||
ThemeVideoIds = new List<Guid>();
|
ThemeVideoIds = new List<Guid>();
|
||||||
Tags = new List<string>();
|
Tags = new List<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Guid> LocalTrailerIds { get; set; }
|
public List<Guid> LocalTrailerIds { get; set; }
|
||||||
|
public List<Guid> RemoteTrailerIds { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the tags.
|
/// Gets or sets the tags.
|
||||||
|
@ -119,5 +121,16 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the trailer ids.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>List<Guid>.</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>
|
/// <value>The path.</value>
|
||||||
string Path { get; set; }
|
string Path { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the file name without extension.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The file name without extension.</value>
|
||||||
|
string FileNameWithoutExtension { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the identifier.
|
/// Gets the identifier.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -4,7 +4,7 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
public interface IHasTrailers
|
public interface IHasTrailers : IHasProviderIds
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the remote trailers.
|
/// Gets or sets the remote trailers.
|
||||||
|
@ -17,5 +17,12 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The local trailer ids.</value>
|
/// <value>The local trailer ids.</value>
|
||||||
List<Guid> LocalTrailerIds { get; set; }
|
List<Guid> LocalTrailerIds { get; set; }
|
||||||
|
List<Guid> RemoteTrailerIds { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the trailer ids.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>List<Guid>.</returns>
|
||||||
|
List<Guid> GetTrailerIds();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,15 +15,19 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class BoxSet
|
/// Class BoxSet
|
||||||
/// </summary>
|
/// </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()
|
public BoxSet()
|
||||||
{
|
{
|
||||||
RemoteTrailers = new List<MediaUrl>();
|
RemoteTrailers = new List<MediaUrl>();
|
||||||
LocalTrailerIds = new List<Guid>();
|
LocalTrailerIds = new List<Guid>();
|
||||||
|
RemoteTrailerIds = new List<Guid>();
|
||||||
|
|
||||||
DisplayOrder = ItemSortBy.PremiereDate;
|
DisplayOrder = ItemSortBy.PremiereDate;
|
||||||
Keywords = new List<string>();
|
Keywords = new List<string>();
|
||||||
|
Shares = new List<Share>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool FilterLinkedChildrenPerUser
|
protected override bool FilterLinkedChildrenPerUser
|
||||||
|
@ -35,6 +39,7 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Guid> LocalTrailerIds { get; set; }
|
public List<Guid> LocalTrailerIds { get; set; }
|
||||||
|
public List<Guid> RemoteTrailerIds { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the remote trailers.
|
/// Gets or sets the remote trailers.
|
||||||
|
@ -76,6 +81,17 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the trailer ids.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>List<Guid>.</returns>
|
||||||
|
public List<Guid> GetTrailerIds()
|
||||||
|
{
|
||||||
|
var list = LocalTrailerIds.ToList();
|
||||||
|
list.AddRange(RemoteTrailerIds);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)
|
public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)
|
||||||
{
|
{
|
||||||
var children = base.GetChildren(user, includeLinkedChildren);
|
var children = base.GetChildren(user, includeLinkedChildren);
|
||||||
|
@ -147,5 +163,20 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||||
|
|
||||||
progress.Report(100);
|
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>();
|
SoundtrackIds = new List<Guid>();
|
||||||
RemoteTrailers = new List<MediaUrl>();
|
RemoteTrailers = new List<MediaUrl>();
|
||||||
LocalTrailerIds = new List<Guid>();
|
LocalTrailerIds = new List<Guid>();
|
||||||
|
RemoteTrailerIds = new List<Guid>();
|
||||||
ThemeSongIds = new List<Guid>();
|
ThemeSongIds = new List<Guid>();
|
||||||
ThemeVideoIds = new List<Guid>();
|
ThemeVideoIds = new List<Guid>();
|
||||||
BoxSetIdList = new List<Guid>();
|
BoxSetIdList = new List<Guid>();
|
||||||
|
@ -49,6 +50,7 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||||
public float? Metascore { get; set; }
|
public float? Metascore { get; set; }
|
||||||
|
|
||||||
public List<Guid> LocalTrailerIds { get; set; }
|
public List<Guid> LocalTrailerIds { get; set; }
|
||||||
|
public List<Guid> RemoteTrailerIds { get; set; }
|
||||||
public List<string> Keywords { get; set; }
|
public List<string> Keywords { get; set; }
|
||||||
|
|
||||||
public List<MediaUrl> RemoteTrailers { 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>
|
/// <value>The name of the TMDB collection.</value>
|
||||||
public string TmdbCollectionName { get; set; }
|
public string TmdbCollectionName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the trailer ids.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>List<Guid>.</returns>
|
||||||
|
public List<Guid> GetTrailerIds()
|
||||||
|
{
|
||||||
|
var list = LocalTrailerIds.ToList();
|
||||||
|
list.AddRange(RemoteTrailerIds);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user data key.
|
/// Gets the user data key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -119,7 +132,7 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||||
|
|
||||||
private async Task<bool> RefreshSpecialFeatures(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
|
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 newItemIds = newItems.Select(i => i.Id).ToList();
|
||||||
|
|
||||||
var itemsChanged = !SpecialFeatureIds.SequenceEqual(newItemIds);
|
var itemsChanged = !SpecialFeatureIds.SequenceEqual(newItemIds);
|
||||||
|
@ -158,6 +171,22 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||||
ProductionYear = yearInName;
|
ProductionYear = yearInName;
|
||||||
hasChanges = true;
|
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;
|
return hasChanges;
|
||||||
|
|
|
@ -89,5 +89,41 @@ namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
return GetItemLookupInfo<MusicVideoInfo>();
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
15
MediaBrowser.Controller/Entities/Share.cs
Normal file
15
MediaBrowser.Controller/Entities/Share.cs
Normal file
|
@ -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>();
|
SoundtrackIds = new List<Guid>();
|
||||||
RemoteTrailers = new List<MediaUrl>();
|
RemoteTrailers = new List<MediaUrl>();
|
||||||
LocalTrailerIds = new List<Guid>();
|
LocalTrailerIds = new List<Guid>();
|
||||||
|
RemoteTrailerIds = new List<Guid>();
|
||||||
DisplaySpecialsWithSeasons = true;
|
DisplaySpecialsWithSeasons = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +58,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||||
public bool DisplaySpecialsWithSeasons { get; set; }
|
public bool DisplaySpecialsWithSeasons { get; set; }
|
||||||
|
|
||||||
public List<Guid> LocalTrailerIds { get; set; }
|
public List<Guid> LocalTrailerIds { get; set; }
|
||||||
|
public List<Guid> RemoteTrailerIds { get; set; }
|
||||||
|
|
||||||
public List<MediaUrl> RemoteTrailers { get; set; }
|
public List<MediaUrl> RemoteTrailers { get; set; }
|
||||||
|
|
||||||
|
@ -109,6 +111,17 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||||
return this.GetProviderId(MetadataProviders.Tvdb) ?? this.GetProviderId(MetadataProviders.Tvcom) ?? base.GetUserDataKey();
|
return this.GetProviderId(MetadataProviders.Tvdb) ?? this.GetProviderId(MetadataProviders.Tvcom) ?? base.GetUserDataKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the trailer ids.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>List<Guid>.</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
|
// Studio, Genre and Rating will all be the same so makes no sense to index by these
|
||||||
protected override IEnumerable<string> GetIndexByOptions()
|
protected override IEnumerable<string> GetIndexByOptions()
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,7 +12,8 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class Trailer
|
/// Class Trailer
|
||||||
/// </summary>
|
/// </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; }
|
public List<Guid> SoundtrackIds { get; set; }
|
||||||
|
|
||||||
|
@ -23,15 +24,12 @@ namespace MediaBrowser.Controller.Entities
|
||||||
RemoteTrailers = new List<MediaUrl>();
|
RemoteTrailers = new List<MediaUrl>();
|
||||||
Taglines = new List<string>();
|
Taglines = new List<string>();
|
||||||
SoundtrackIds = new List<Guid>();
|
SoundtrackIds = new List<Guid>();
|
||||||
LocalTrailerIds = new List<Guid>();
|
|
||||||
Keywords = new List<string>();
|
Keywords = new List<string>();
|
||||||
ProductionLocations = new List<string>();
|
ProductionLocations = new List<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public float? Metascore { get; set; }
|
public float? Metascore { get; set; }
|
||||||
|
|
||||||
public List<Guid> LocalTrailerIds { get; set; }
|
|
||||||
|
|
||||||
public List<MediaUrl> RemoteTrailers { get; set; }
|
public List<MediaUrl> RemoteTrailers { get; set; }
|
||||||
|
|
||||||
public List<string> Keywords { get; set; }
|
public List<string> Keywords { get; set; }
|
||||||
|
|
|
@ -4,6 +4,7 @@ using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
using MediaBrowser.Model.Connect;
|
using MediaBrowser.Model.Connect;
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
|
using MediaBrowser.Model.Users;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
|
@ -63,8 +63,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
CollectionType.Books,
|
CollectionType.Books,
|
||||||
CollectionType.HomeVideos,
|
CollectionType.HomeVideos,
|
||||||
CollectionType.Photos,
|
CollectionType.Photos
|
||||||
CollectionType.Trailers
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var collectionFolder = folder as ICollectionFolder;
|
var collectionFolder = folder as ICollectionFolder;
|
||||||
|
|
|
@ -1428,7 +1428,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
var hasTrailers = item as IHasTrailers;
|
var hasTrailers = item as IHasTrailers;
|
||||||
if (hasTrailers != null)
|
if (hasTrailers != null)
|
||||||
{
|
{
|
||||||
trailerCount = hasTrailers.LocalTrailerIds.Count;
|
trailerCount = hasTrailers.GetTrailerIds().Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ok = val ? trailerCount > 0 : trailerCount == 0;
|
var ok = val ? trailerCount > 0 : trailerCount == 0;
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Controller.Resolvers;
|
|
||||||
using MediaBrowser.Model.Dto;
|
using MediaBrowser.Model.Dto;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.MediaInfo;
|
using MediaBrowser.Model.MediaInfo;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -28,12 +26,11 @@ namespace MediaBrowser.Controller.Entities
|
||||||
IHasPreferredMetadataLanguage,
|
IHasPreferredMetadataLanguage,
|
||||||
IThemeMedia
|
IThemeMedia
|
||||||
{
|
{
|
||||||
public bool IsMultiPart { get; set; }
|
|
||||||
public bool HasLocalAlternateVersions { get; set; }
|
|
||||||
public Guid? PrimaryVersionId { get; set; }
|
public Guid? PrimaryVersionId { get; set; }
|
||||||
|
|
||||||
public List<Guid> AdditionalPartIds { get; set; }
|
public List<string> AdditionalParts { get; set; }
|
||||||
public List<Guid> LocalAlternateVersionIds { get; set; }
|
public List<string> LocalAlternateVersions { get; set; }
|
||||||
|
public List<LinkedChild> LinkedAlternateVersions { get; set; }
|
||||||
|
|
||||||
public bool IsThemeMedia { get; set; }
|
public bool IsThemeMedia { get; set; }
|
||||||
|
|
||||||
|
@ -60,8 +57,8 @@ namespace MediaBrowser.Controller.Entities
|
||||||
public Video()
|
public Video()
|
||||||
{
|
{
|
||||||
PlayableStreamFileNames = new List<string>();
|
PlayableStreamFileNames = new List<string>();
|
||||||
AdditionalPartIds = new List<Guid>();
|
AdditionalParts = new List<string>();
|
||||||
LocalAlternateVersionIds = new List<Guid>();
|
LocalAlternateVersions = new List<string>();
|
||||||
Tags = new List<string>();
|
Tags = new List<string>();
|
||||||
SubtitleFiles = new List<string>();
|
SubtitleFiles = new List<string>();
|
||||||
LinkedAlternateVersions = new List<LinkedChild>();
|
LinkedAlternateVersions = new List<LinkedChild>();
|
||||||
|
@ -78,11 +75,31 @@ namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
get
|
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>
|
/// <summary>
|
||||||
/// Gets the linked children.
|
/// Gets the linked children.
|
||||||
|
@ -90,7 +107,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||||
public IEnumerable<Video> GetAlternateVersions()
|
public IEnumerable<Video> GetAlternateVersions()
|
||||||
{
|
{
|
||||||
var filesWithinSameDirectory = LocalAlternateVersionIds
|
var filesWithinSameDirectory = GetLocalAlternateVersionIds()
|
||||||
.Select(i => LibraryManager.GetItemById(i))
|
.Select(i => LibraryManager.GetItemById(i))
|
||||||
.Where(i => i != null)
|
.Where(i => i != null)
|
||||||
.OfType<Video>();
|
.OfType<Video>();
|
||||||
|
@ -116,7 +133,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// <returns>IEnumerable{Video}.</returns>
|
/// <returns>IEnumerable{Video}.</returns>
|
||||||
public IEnumerable<Video> GetAdditionalParts()
|
public IEnumerable<Video> GetAdditionalParts()
|
||||||
{
|
{
|
||||||
return AdditionalPartIds
|
return GetAdditionalPartIds()
|
||||||
.Select(i => LibraryManager.GetItemById(i))
|
.Select(i => LibraryManager.GetItemById(i))
|
||||||
.Where(i => i != null)
|
.Where(i => i != null)
|
||||||
.OfType<Video>()
|
.OfType<Video>()
|
||||||
|
@ -200,7 +217,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (IsMultiPart)
|
if (IsStacked)
|
||||||
{
|
{
|
||||||
return System.IO.Path.GetDirectoryName(Path);
|
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; }
|
public string MainFeaturePlaylistName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -263,37 +320,34 @@ namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
var hasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
|
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
|
// Must have a parent to have additional parts or alternate versions
|
||||||
// In other words, it must be part of the Parent/Child tree
|
// In other words, it must be part of the Parent/Child tree
|
||||||
// The additional parts won't have additional parts themselves
|
// The additional parts won't have additional parts themselves
|
||||||
if (LocationType == LocationType.FileSystem && Parent != null)
|
if (LocationType == LocationType.FileSystem && Parent != null)
|
||||||
{
|
{
|
||||||
if (IsMultiPart)
|
if (!IsStacked)
|
||||||
{
|
|
||||||
var additionalPartsChanged = await RefreshAdditionalParts(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (additionalPartsChanged)
|
|
||||||
{
|
|
||||||
hasChanges = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
RefreshLinkedAlternateVersions();
|
RefreshLinkedAlternateVersions();
|
||||||
|
|
||||||
var additionalPartsChanged = await RefreshAlternateVersionsWithinSameDirectory(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
|
var tasks = LocalAlternateVersions
|
||||||
|
.Select(i => RefreshMetadataForOwnedVideo(options, i, cancellationToken));
|
||||||
|
|
||||||
if (additionalPartsChanged)
|
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||||
{
|
|
||||||
hasChanges = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasChanges;
|
return hasChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool RefreshLinkedAlternateVersions()
|
private void RefreshLinkedAlternateVersions()
|
||||||
{
|
{
|
||||||
foreach (var child in LinkedAlternateVersions)
|
foreach (var child in LinkedAlternateVersions)
|
||||||
{
|
{
|
||||||
|
@ -303,111 +357,13 @@ namespace MediaBrowser.Controller.Entities
|
||||||
child.ItemId = null;
|
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)
|
public override async Task UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
await base.UpdateToRepository(updateReason, cancellationToken).ConfigureAwait(false);
|
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.ImageInfos = ImageInfos;
|
||||||
item.Overview = Overview;
|
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()
|
public override IEnumerable<string> GetDeletePaths()
|
||||||
{
|
{
|
||||||
if (!IsInMixedFolder)
|
if (!IsInMixedFolder)
|
||||||
|
|
|
@ -24,7 +24,9 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// <param name="parent">The parent.</param>
|
/// <param name="parent">The parent.</param>
|
||||||
/// <param name="collectionType">Type of the collection.</param>
|
/// <param name="collectionType">Type of the collection.</param>
|
||||||
/// <returns>BaseItem.</returns>
|
/// <returns>BaseItem.</returns>
|
||||||
BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null, string collectionType = null);
|
BaseItem ResolvePath(FileSystemInfo fileInfo,
|
||||||
|
Folder parent = null,
|
||||||
|
string collectionType = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves a set of files into a list of BaseItem
|
/// 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="parent">The parent.</param>
|
||||||
/// <param name="collectionType">Type of the collection.</param>
|
/// <param name="collectionType">Type of the collection.</param>
|
||||||
/// <returns>List{``0}.</returns>
|
/// <returns>List{``0}.</returns>
|
||||||
List<T> ResolvePaths<T>(IEnumerable<FileSystemInfo> files, IDirectoryService directoryService, Folder parent, string collectionType = null)
|
IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemInfo> files,
|
||||||
where T : BaseItem;
|
IDirectoryService directoryService,
|
||||||
|
Folder parent, string
|
||||||
|
collectionType = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the root folder.
|
/// Gets the root folder.
|
||||||
|
@ -229,46 +233,6 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// <returns>BaseItem.</returns>
|
/// <returns>BaseItem.</returns>
|
||||||
BaseItem RetrieveItem(Guid id);
|
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>
|
/// <summary>
|
||||||
/// Occurs when [item added].
|
/// Occurs when [item added].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -405,14 +369,31 @@ namespace MediaBrowser.Controller.Library
|
||||||
ItemLookupInfo ParseName(string name);
|
ItemLookupInfo ParseName(string name);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the additional parts.
|
/// Gets the new item identifier.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="file">The file.</param>
|
/// <param name="key">The key.</param>
|
||||||
/// <param name="type">The type.</param>
|
/// <param name="type">The type.</param>
|
||||||
/// <param name="files">The files.</param>
|
/// <returns>Guid.</returns>
|
||||||
/// <returns>IEnumerable<System.String>.</returns>
|
Guid GetNewItemId(string key, Type type);
|
||||||
IEnumerable<FileSystemInfo> GetAdditionalParts(string file,
|
|
||||||
VideoType type,
|
/// <summary>
|
||||||
IEnumerable<FileSystemInfo> files);
|
/// 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<Trailer>.</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<Video>.</returns>
|
||||||
|
IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemInfo> fileSystemChildren,
|
||||||
|
IDirectoryService directoryService);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
|
using MediaBrowser.Controller.Playlists;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Library
|
namespace MediaBrowser.Controller.Library
|
||||||
|
@ -28,6 +29,13 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// <returns>IEnumerable{Audio}.</returns>
|
/// <returns>IEnumerable{Audio}.</returns>
|
||||||
IEnumerable<Audio> GetInstantMixFromAlbum(MusicAlbum item, User user);
|
IEnumerable<Audio> GetInstantMixFromAlbum(MusicAlbum item, User user);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// Gets the instant mix from playlist.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The item.</param>
|
||||||
|
/// <param name="user">The user.</param>
|
||||||
|
/// <returns>IEnumerable<Audio>.</returns>
|
||||||
|
IEnumerable<Audio> GetInstantMixFromPlaylist(Playlist item, User user);
|
||||||
|
/// <summary>
|
||||||
/// Gets the instant mix from genre.
|
/// Gets the instant mix from genre.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="genres">The genres.</param>
|
/// <param name="genres">The genres.</param>
|
||||||
|
|
|
@ -164,5 +164,19 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// <param name="pin">The pin.</param>
|
/// <param name="pin">The pin.</param>
|
||||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||||
Task<PinRedeemResult> RedeemPasswordResetPin(string pin);
|
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;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Controller.Channels;
|
using MediaBrowser.Controller.Channels;
|
||||||
|
using MediaBrowser.Controller.Drawing;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.LiveTv
|
namespace MediaBrowser.Controller.LiveTv
|
||||||
{
|
{
|
||||||
|
@ -109,7 +110,7 @@ namespace MediaBrowser.Controller.LiveTv
|
||||||
/// <param name="channelId">The channel identifier.</param>
|
/// <param name="channelId">The channel identifier.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{Stream}.</returns>
|
/// <returns>Task{Stream}.</returns>
|
||||||
Task<StreamResponseInfo> GetChannelImageAsync(string channelId, CancellationToken cancellationToken);
|
Task<ImageStream> GetChannelImageAsync(string channelId, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the recording image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to RecordingInfo
|
/// 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="recordingId">The recording identifier.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{ImageResponseInfo}.</returns>
|
/// <returns>Task{ImageResponseInfo}.</returns>
|
||||||
Task<StreamResponseInfo> GetRecordingImageAsync(string recordingId, CancellationToken cancellationToken);
|
Task<ImageStream> GetRecordingImageAsync(string recordingId, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the program image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to ProgramInfo
|
/// 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="channelId">The channel identifier.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{ImageResponseInfo}.</returns>
|
/// <returns>Task{ImageResponseInfo}.</returns>
|
||||||
Task<StreamResponseInfo> GetProgramImageAsync(string programId, string channelId, CancellationToken cancellationToken);
|
Task<ImageStream> GetProgramImageAsync(string programId, string channelId, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the recordings asynchronous.
|
/// 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" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Drawing" />
|
|
||||||
<Reference Include="System.Net" />
|
<Reference Include="System.Net" />
|
||||||
<Reference Include="System.Runtime.Serialization" />
|
<Reference Include="System.Runtime.Serialization" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
@ -106,7 +105,6 @@
|
||||||
<Compile Include="Devices\IDeviceRepository.cs" />
|
<Compile Include="Devices\IDeviceRepository.cs" />
|
||||||
<Compile Include="Dlna\ControlRequest.cs" />
|
<Compile Include="Dlna\ControlRequest.cs" />
|
||||||
<Compile Include="Dlna\ControlResponse.cs" />
|
<Compile Include="Dlna\ControlResponse.cs" />
|
||||||
<Compile Include="Dlna\DlnaIconResponse.cs" />
|
|
||||||
<Compile Include="Dlna\EventSubscriptionResponse.cs" />
|
<Compile Include="Dlna\EventSubscriptionResponse.cs" />
|
||||||
<Compile Include="Dlna\IConnectionManager.cs" />
|
<Compile Include="Dlna\IConnectionManager.cs" />
|
||||||
<Compile Include="Dlna\IContentDirectory.cs" />
|
<Compile Include="Dlna\IContentDirectory.cs" />
|
||||||
|
@ -114,9 +112,9 @@
|
||||||
<Compile Include="Dlna\IEventManager.cs" />
|
<Compile Include="Dlna\IEventManager.cs" />
|
||||||
<Compile Include="Dlna\IUpnpService.cs" />
|
<Compile Include="Dlna\IUpnpService.cs" />
|
||||||
<Compile Include="Drawing\IImageProcessor.cs" />
|
<Compile Include="Drawing\IImageProcessor.cs" />
|
||||||
<Compile Include="Drawing\ImageFormat.cs" />
|
|
||||||
<Compile Include="Drawing\ImageProcessingOptions.cs" />
|
<Compile Include="Drawing\ImageProcessingOptions.cs" />
|
||||||
<Compile Include="Drawing\ImageProcessorExtensions.cs" />
|
<Compile Include="Drawing\ImageProcessorExtensions.cs" />
|
||||||
|
<Compile Include="Drawing\ImageStream.cs" />
|
||||||
<Compile Include="Dto\IDtoService.cs" />
|
<Compile Include="Dto\IDtoService.cs" />
|
||||||
<Compile Include="Entities\AdultVideo.cs" />
|
<Compile Include="Entities\AdultVideo.cs" />
|
||||||
<Compile Include="Entities\Audio\IHasAlbumArtist.cs" />
|
<Compile Include="Entities\Audio\IHasAlbumArtist.cs" />
|
||||||
|
@ -164,6 +162,7 @@
|
||||||
<Compile Include="Entities\IHasAwards.cs" />
|
<Compile Include="Entities\IHasAwards.cs" />
|
||||||
<Compile Include="Entities\Photo.cs" />
|
<Compile Include="Entities\Photo.cs" />
|
||||||
<Compile Include="Entities\PhotoAlbum.cs" />
|
<Compile Include="Entities\PhotoAlbum.cs" />
|
||||||
|
<Compile Include="Entities\Share.cs" />
|
||||||
<Compile Include="Entities\UserView.cs" />
|
<Compile Include="Entities\UserView.cs" />
|
||||||
<Compile Include="Entities\UserViewBuilder.cs" />
|
<Compile Include="Entities\UserViewBuilder.cs" />
|
||||||
<Compile Include="FileOrganization\IFileOrganizationService.cs" />
|
<Compile Include="FileOrganization\IFileOrganizationService.cs" />
|
||||||
|
@ -192,7 +191,6 @@
|
||||||
<Compile Include="LiveTv\LiveTvException.cs" />
|
<Compile Include="LiveTv\LiveTvException.cs" />
|
||||||
<Compile Include="LiveTv\LiveTvServiceStatusInfo.cs" />
|
<Compile Include="LiveTv\LiveTvServiceStatusInfo.cs" />
|
||||||
<Compile Include="LiveTv\LiveTvTunerInfo.cs" />
|
<Compile Include="LiveTv\LiveTvTunerInfo.cs" />
|
||||||
<Compile Include="LiveTv\StreamResponseInfo.cs" />
|
|
||||||
<Compile Include="LiveTv\LiveTvProgram.cs" />
|
<Compile Include="LiveTv\LiveTvProgram.cs" />
|
||||||
<Compile Include="LiveTv\LiveTvVideoRecording.cs" />
|
<Compile Include="LiveTv\LiveTvVideoRecording.cs" />
|
||||||
<Compile Include="LiveTv\ProgramInfo.cs" />
|
<Compile Include="LiveTv\ProgramInfo.cs" />
|
||||||
|
@ -270,7 +268,6 @@
|
||||||
<Compile Include="Providers\MetadataStatus.cs" />
|
<Compile Include="Providers\MetadataStatus.cs" />
|
||||||
<Compile Include="Providers\ISeriesOrderManager.cs" />
|
<Compile Include="Providers\ISeriesOrderManager.cs" />
|
||||||
<Compile Include="Session\ISessionManager.cs" />
|
<Compile Include="Session\ISessionManager.cs" />
|
||||||
<Compile Include="Drawing\ImageExtensions.cs" />
|
|
||||||
<Compile Include="Entities\AggregateFolder.cs" />
|
<Compile Include="Entities\AggregateFolder.cs" />
|
||||||
<Compile Include="Entities\Audio\Audio.cs" />
|
<Compile Include="Entities\Audio\Audio.cs" />
|
||||||
<Compile Include="Entities\Audio\MusicAlbum.cs" />
|
<Compile Include="Entities\Audio\MusicAlbum.cs" />
|
||||||
|
|
|
@ -10,12 +10,6 @@ namespace MediaBrowser.Controller.Persistence
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IUserRepository : IRepository
|
public interface IUserRepository : IRepository
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Opens the connection to the repository
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Task.</returns>
|
|
||||||
Task Initialize();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes the user.
|
/// Deletes the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -11,10 +11,17 @@ using System.Runtime.Serialization;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Playlists
|
namespace MediaBrowser.Controller.Playlists
|
||||||
{
|
{
|
||||||
public class Playlist : Folder
|
public class Playlist : Folder, IHasShares
|
||||||
{
|
{
|
||||||
public string OwnerUserId { get; set; }
|
public string OwnerUserId { get; set; }
|
||||||
|
|
||||||
|
public List<Share> Shares { get; set; }
|
||||||
|
|
||||||
|
public Playlist()
|
||||||
|
{
|
||||||
|
Shares = new List<Share>();
|
||||||
|
}
|
||||||
|
|
||||||
[IgnoreDataMember]
|
[IgnoreDataMember]
|
||||||
protected override bool FilterLinkedChildrenPerUser
|
protected override bool FilterLinkedChildrenPerUser
|
||||||
{
|
{
|
||||||
|
@ -166,7 +173,15 @@ namespace MediaBrowser.Controller.Playlists
|
||||||
|
|
||||||
public override bool IsVisible(User user)
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "Shares":
|
||||||
|
{
|
||||||
|
using (var subtree = reader.ReadSubtree())
|
||||||
|
{
|
||||||
|
var hasShares = item as IHasShares;
|
||||||
|
if (hasShares != null)
|
||||||
|
{
|
||||||
|
FetchFromSharesNode(subtree, hasShares);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "Format3D":
|
case "Format3D":
|
||||||
{
|
{
|
||||||
var video = item as Video;
|
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)
|
private void FetchFromCountriesNode(XmlReader reader, T item)
|
||||||
{
|
{
|
||||||
reader.MoveToContent();
|
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;
|
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.Drawing;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using System.Drawing;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Providers
|
namespace MediaBrowser.Controller.Providers
|
||||||
|
@ -49,6 +49,6 @@ namespace MediaBrowser.Controller.Providers
|
||||||
/// <param name="imageIndex">Index of the image.</param>
|
/// <param name="imageIndex">Index of the image.</param>
|
||||||
/// <returns>Task{Image}.</returns>
|
/// <returns>Task{Image}.</returns>
|
||||||
/// <exception cref="System.ArgumentNullException"></exception>
|
/// <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 MediaBrowser.Model.Entities;
|
||||||
using System.Collections.Generic;
|
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 MediaBrowser.Model.Entities;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Resolvers
|
namespace MediaBrowser.Controller.Resolvers
|
||||||
{
|
{
|
||||||
|
@ -20,4 +23,24 @@ namespace MediaBrowser.Controller.Resolvers
|
||||||
/// <value>The priority.</value>
|
/// <value>The priority.</value>
|
||||||
ResolverPriority Priority { get; }
|
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.Controller.Entities;
|
using MediaBrowser.Model.Dlna;
|
||||||
using MediaBrowser.Model.Devices;
|
|
||||||
using MediaBrowser.Model.Querying;
|
using MediaBrowser.Model.Querying;
|
||||||
using MediaBrowser.Model.Sync;
|
using MediaBrowser.Model.Sync;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -53,5 +52,12 @@ namespace MediaBrowser.Controller.Sync
|
||||||
/// <param name="item">The item.</param>
|
/// <param name="item">The item.</param>
|
||||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||||
bool SupportsSync(BaseItem item);
|
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>
|
/// <returns>Task.</returns>
|
||||||
Task Update(SyncJob job);
|
Task Update(SyncJob job);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes the job.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The identifier.</param>
|
||||||
|
/// <returns>Task.</returns>
|
||||||
|
Task DeleteJob(string id);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the jobs.
|
/// Gets the jobs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -54,5 +61,12 @@ namespace MediaBrowser.Controller.Sync
|
||||||
/// <param name="jobItem">The job item.</param>
|
/// <param name="jobItem">The job item.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task Update(SyncJobItem jobItem);
|
Task Update(SyncJobItem jobItem);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the job items.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">The query.</param>
|
||||||
|
/// <returns>IEnumerable<SyncJobItem>.</returns>
|
||||||
|
QueryResult<SyncJobItem> GetJobItems(SyncJobItemQuery query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
|
using MediaBrowser.Controller.Channels;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Dlna;
|
using MediaBrowser.Controller.Dlna;
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
|
@ -23,6 +24,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
private readonly IUserManager _userManager;
|
private readonly IUserManager _userManager;
|
||||||
private readonly ILocalizationManager _localization;
|
private readonly ILocalizationManager _localization;
|
||||||
|
private readonly IChannelManager _channelManager;
|
||||||
|
|
||||||
public ContentDirectory(IDlnaManager dlna,
|
public ContentDirectory(IDlnaManager dlna,
|
||||||
IUserDataManager userDataManager,
|
IUserDataManager userDataManager,
|
||||||
|
@ -31,7 +33,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||||
IServerConfigurationManager config,
|
IServerConfigurationManager config,
|
||||||
IUserManager userManager,
|
IUserManager userManager,
|
||||||
ILogger logger,
|
ILogger logger,
|
||||||
IHttpClient httpClient, ILocalizationManager localization)
|
IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager)
|
||||||
: base(logger, httpClient)
|
: base(logger, httpClient)
|
||||||
{
|
{
|
||||||
_dlna = dlna;
|
_dlna = dlna;
|
||||||
|
@ -41,6 +43,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||||
_config = config;
|
_config = config;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_localization = localization;
|
_localization = localization;
|
||||||
|
_channelManager = channelManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int SystemUpdateId
|
private int SystemUpdateId
|
||||||
|
@ -77,7 +80,8 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||||
user,
|
user,
|
||||||
SystemUpdateId,
|
SystemUpdateId,
|
||||||
_config,
|
_config,
|
||||||
_localization)
|
_localization,
|
||||||
|
_channelManager)
|
||||||
.ProcessControlRequest(request);
|
.ProcessControlRequest(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
|
using MediaBrowser.Controller.Channels;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Movies;
|
using MediaBrowser.Controller.Entities.Movies;
|
||||||
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Localization;
|
using MediaBrowser.Controller.Localization;
|
||||||
using MediaBrowser.Dlna.Didl;
|
using MediaBrowser.Dlna.Didl;
|
||||||
using MediaBrowser.Dlna.Server;
|
using MediaBrowser.Dlna.Server;
|
||||||
using MediaBrowser.Dlna.Service;
|
using MediaBrowser.Dlna.Service;
|
||||||
|
using MediaBrowser.Model.Channels;
|
||||||
using MediaBrowser.Model.Dlna;
|
using MediaBrowser.Model.Dlna;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
|
@ -26,6 +29,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||||
public class ControlHandler : BaseControlHandler
|
public class ControlHandler : BaseControlHandler
|
||||||
{
|
{
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
private readonly IChannelManager _channelManager;
|
||||||
private readonly IUserDataManager _userDataManager;
|
private readonly IUserDataManager _userDataManager;
|
||||||
private readonly User _user;
|
private readonly User _user;
|
||||||
|
|
||||||
|
@ -41,13 +45,14 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||||
|
|
||||||
private readonly DeviceProfile _profile;
|
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)
|
: base(config, logger)
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_userDataManager = userDataManager;
|
_userDataManager = userDataManager;
|
||||||
_user = user;
|
_user = user;
|
||||||
_systemUpdateId = systemUpdateId;
|
_systemUpdateId = systemUpdateId;
|
||||||
|
_channelManager = channelManager;
|
||||||
_profile = profile;
|
_profile = profile;
|
||||||
|
|
||||||
_didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, userDataManager, localization);
|
_didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, userDataManager, localization);
|
||||||
|
@ -208,14 +213,15 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||||
var serverItem = GetItemFromObjectId(id, user);
|
var serverItem = GetItemFromObjectId(id, user);
|
||||||
var item = serverItem.Item;
|
var item = serverItem.Item;
|
||||||
|
|
||||||
var totalCount = 0;
|
int totalCount;
|
||||||
|
|
||||||
if (string.Equals(flag, "BrowseMetadata"))
|
if (string.Equals(flag, "BrowseMetadata"))
|
||||||
{
|
{
|
||||||
|
totalCount = 1;
|
||||||
|
|
||||||
if (item.IsFolder || serverItem.StubType.HasValue)
|
if (item.IsFolder || serverItem.StubType.HasValue)
|
||||||
{
|
{
|
||||||
var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requested).ConfigureAwait(false));
|
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));
|
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)
|
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;
|
.TotalRecordCount;
|
||||||
|
|
||||||
result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, i, displayStubType, item, childCount, filter));
|
result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, childItem, displayStubType, item, childCount, filter));
|
||||||
}
|
}
|
||||||
else
|
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)
|
private async Task<IEnumerable<KeyValuePair<string, string>>> HandleSearch(Headers sparams, User user, string deviceId)
|
||||||
{
|
{
|
||||||
var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", ""));
|
var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", ""));
|
||||||
|
@ -408,18 +397,51 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||||
}).ConfigureAwait(false);
|
}).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)
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
return ApplyPaging(result, startIndex, limit);
|
||||||
|
}
|
||||||
|
if (stubType.Value == StubType.Folder)
|
||||||
|
{
|
||||||
|
var movie = item as Movie;
|
||||||
if (movie != null)
|
if (movie != null)
|
||||||
{
|
{
|
||||||
return await GetMovieItems(movie).ConfigureAwait(false);
|
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;
|
var folder = (Folder)item;
|
||||||
|
|
||||||
var sortOrders = new List<string>();
|
var sortOrders = new List<string>();
|
||||||
|
@ -428,7 +450,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||||
sortOrders.Add(ItemSortBy.SortName);
|
sortOrders.Add(ItemSortBy.SortName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await folder.GetItems(new InternalItemsQuery
|
var queryResult = await folder.GetItems(new InternalItemsQuery
|
||||||
{
|
{
|
||||||
Limit = limit,
|
Limit = limit,
|
||||||
StartIndex = startIndex,
|
StartIndex = startIndex,
|
||||||
|
@ -438,22 +460,138 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||||
Filter = FilterUnsupportedContent
|
Filter = FilterUnsupportedContent
|
||||||
|
|
||||||
}).ConfigureAwait(false);
|
}).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 Task<QueryResult<BaseItem>> GetMovieItems(Movie item)
|
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 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>();
|
var list = new List<BaseItem>();
|
||||||
|
|
||||||
list.Add(item);
|
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.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(),
|
Item = item,
|
||||||
TotalRecordCount = list.Count
|
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;
|
stubType = StubType.Folder;
|
||||||
id = id.Split(new[] { '_' }, 2)[1];
|
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))
|
if (Guid.TryParse(id, out itemId))
|
||||||
{
|
{
|
||||||
|
@ -524,6 +667,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
||||||
|
|
||||||
public enum StubType
|
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;
|
return element;
|
||||||
}
|
}
|
||||||
|
@ -293,8 +293,17 @@ namespace MediaBrowser.Dlna.Didl
|
||||||
container.AppendChild(res);
|
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 episode = item as Episode;
|
||||||
var season = context as Season;
|
var season = context as Season;
|
||||||
|
|
||||||
|
@ -460,7 +469,7 @@ namespace MediaBrowser.Dlna.Didl
|
||||||
|
|
||||||
AddCommonFields(folder, stubType, null, container, filter);
|
AddCommonFields(folder, stubType, null, container, filter);
|
||||||
|
|
||||||
AddCover(folder, container);
|
AddCover(folder, stubType, container);
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
@ -491,7 +500,7 @@ namespace MediaBrowser.Dlna.Didl
|
||||||
// MediaMonkey for example won't display content without a title
|
// MediaMonkey for example won't display content without a title
|
||||||
//if (filter.Contains("dc: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));
|
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);
|
var imageInfo = GetImageInfo(item);
|
||||||
|
|
||||||
if (imageInfo == null)
|
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,
|
private void AddImageResElement(BaseItem item,
|
||||||
XmlElement element,
|
XmlElement element,
|
||||||
int maxWidth,
|
int maxWidth,
|
||||||
|
|
|
@ -8,6 +8,7 @@ using MediaBrowser.Dlna.Profiles;
|
||||||
using MediaBrowser.Dlna.Server;
|
using MediaBrowser.Dlna.Server;
|
||||||
using MediaBrowser.Model.Dlna;
|
using MediaBrowser.Model.Dlna;
|
||||||
using MediaBrowser.Model.Dlna.Profiles;
|
using MediaBrowser.Model.Dlna.Profiles;
|
||||||
|
using MediaBrowser.Model.Drawing;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
using System;
|
using System;
|
||||||
|
@ -469,13 +470,13 @@ namespace MediaBrowser.Dlna
|
||||||
return new DescriptionXmlBuilder(profile, serverUuId, "").GetXml();
|
return new DescriptionXmlBuilder(profile, serverUuId, "").GetXml();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DlnaIconResponse GetIcon(string filename)
|
public ImageStream GetIcon(string filename)
|
||||||
{
|
{
|
||||||
var format = filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
|
var format = filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
|
||||||
? ImageFormat.Png
|
? ImageFormat.Png
|
||||||
: ImageFormat.Jpg;
|
: ImageFormat.Jpg;
|
||||||
|
|
||||||
return new DlnaIconResponse
|
return new ImageStream
|
||||||
{
|
{
|
||||||
Format = format,
|
Format = format,
|
||||||
Stream = GetType().Assembly.GetManifestResourceStream("MediaBrowser.Dlna.Images." + filename.ToLower())
|
Stream = GetType().Assembly.GetManifestResourceStream("MediaBrowser.Dlna.Images." + filename.ToLower())
|
||||||
|
@ -522,10 +523,9 @@ namespace MediaBrowser.Dlna
|
||||||
new LgTvProfile(),
|
new LgTvProfile(),
|
||||||
new Foobar2000Profile(),
|
new Foobar2000Profile(),
|
||||||
new MediaMonkeyProfile(),
|
new MediaMonkeyProfile(),
|
||||||
new Windows81Profile(),
|
//new Windows81Profile(),
|
||||||
//new WindowsMediaCenterProfile(),
|
//new WindowsMediaCenterProfile(),
|
||||||
new WindowsPhoneProfile(),
|
//new WindowsPhoneProfile(),
|
||||||
new AndroidProfile(),
|
|
||||||
new DirectTvProfile(),
|
new DirectTvProfile(),
|
||||||
new DishHopperJoeyProfile(),
|
new DishHopperJoeyProfile(),
|
||||||
new DefaultProfile(),
|
new DefaultProfile(),
|
||||||
|
|
BIN
MediaBrowser.Dlna/Images/people48.jpg
Normal file
BIN
MediaBrowser.Dlna/Images/people48.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
MediaBrowser.Dlna/Images/people48.png
Normal file
BIN
MediaBrowser.Dlna/Images/people48.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 688 B |
BIN
MediaBrowser.Dlna/Images/people480.jpg
Normal file
BIN
MediaBrowser.Dlna/Images/people480.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.5 KiB |
BIN
MediaBrowser.Dlna/Images/people480.png
Normal file
BIN
MediaBrowser.Dlna/Images/people480.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
|
@ -180,11 +180,6 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Profiles\Xml\MediaMonkey.xml" />
|
<EmbeddedResource Include="Profiles\Xml\MediaMonkey.xml" />
|
||||||
</ItemGroup>
|
</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>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Images\logo240.jpg" />
|
<EmbeddedResource Include="Images\logo240.jpg" />
|
||||||
<EmbeddedResource Include="Images\logo240.png" />
|
<EmbeddedResource Include="Images\logo240.png" />
|
||||||
|
@ -198,6 +193,14 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Profiles\Xml\Popcorn Hour.xml" />
|
<EmbeddedResource Include="Profiles\Xml\Popcorn Hour.xml" />
|
||||||
</ItemGroup>
|
</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" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- 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.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
|
|
@ -91,7 +91,7 @@ namespace MediaBrowser.Dlna.Profiles
|
||||||
},
|
},
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Container = "3gpp",
|
Container = "3gp",
|
||||||
VideoCodec = "h264,mpeg4",
|
VideoCodec = "h264,mpeg4",
|
||||||
AudioCodec = "aac,he-aac",
|
AudioCodec = "aac,he-aac",
|
||||||
Type = DlnaProfileType.Video
|
Type = DlnaProfileType.Video
|
||||||
|
|
|
@ -16,7 +16,12 @@ namespace MediaBrowser.Dlna.Profiles
|
||||||
Identification = new DeviceIdentification
|
Identification = new DeviceIdentification
|
||||||
{
|
{
|
||||||
ModelName = "Xbox One",
|
ModelName = "Xbox One",
|
||||||
FriendlyName = "Xbox-SystemOS"
|
FriendlyName = "Xbox-SystemOS",
|
||||||
|
|
||||||
|
Headers = new[]
|
||||||
|
{
|
||||||
|
new HttpHeaderInfo {Name = "User-Agent", Value = "NSPlayer", Match = HeaderMatchType.Substring}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TranscodingProfiles = new[]
|
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="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="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="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="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="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" />
|
<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>
|
<Identification>
|
||||||
<FriendlyName>Xbox-SystemOS</FriendlyName>
|
<FriendlyName>Xbox-SystemOS</FriendlyName>
|
||||||
<ModelName>Xbox One</ModelName>
|
<ModelName>Xbox One</ModelName>
|
||||||
<Headers />
|
<Headers>
|
||||||
|
<HttpHeaderInfo name="User-Agent" value="NSPlayer" match="Substring" />
|
||||||
|
</Headers>
|
||||||
</Identification>
|
</Identification>
|
||||||
<FriendlyName>Media Browser</FriendlyName>
|
<FriendlyName>Media Browser</FriendlyName>
|
||||||
<Manufacturer>Media Browser</Manufacturer>
|
<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)
|
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;
|
PopulatePrimaryImages(item, images, files, imagePrefix, isInMixedFolder);
|
||||||
if (baseItem != null && baseItem.IsInMixedFolder)
|
|
||||||
{
|
|
||||||
imagePrefix = _fileSystem.GetFileNameWithoutExtension(item.Path) + "-";
|
|
||||||
}
|
|
||||||
|
|
||||||
PopulatePrimaryImages(item, images, files, imagePrefix);
|
AddImage(files, images, "logo", imagePrefix, isInMixedFolder, ImageType.Logo);
|
||||||
PopulateBackdrops(item, images, files, imagePrefix, directoryService);
|
AddImage(files, images, "clearart", imagePrefix, isInMixedFolder, ImageType.Art);
|
||||||
PopulateScreenshots(images, files, imagePrefix);
|
AddImage(files, images, "disc", imagePrefix, isInMixedFolder, ImageType.Disc);
|
||||||
|
AddImage(files, images, "cdart", imagePrefix, isInMixedFolder, ImageType.Disc);
|
||||||
AddImage(files, images, imagePrefix + "logo", ImageType.Logo);
|
AddImage(files, images, "box", imagePrefix, isInMixedFolder, ImageType.Box);
|
||||||
AddImage(files, images, imagePrefix + "clearart", ImageType.Art);
|
AddImage(files, images, "back", imagePrefix, isInMixedFolder, ImageType.BoxRear);
|
||||||
AddImage(files, images, imagePrefix + "disc", ImageType.Disc);
|
AddImage(files, images, "boxrear", imagePrefix, isInMixedFolder, ImageType.BoxRear);
|
||||||
AddImage(files, images, imagePrefix + "cdart", ImageType.Disc);
|
AddImage(files, images, "menu", imagePrefix, isInMixedFolder, ImageType.Menu);
|
||||||
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);
|
|
||||||
|
|
||||||
// Banner
|
// Banner
|
||||||
AddImage(files, images, imagePrefix + "banner", ImageType.Banner);
|
AddImage(files, images, "banner", imagePrefix, isInMixedFolder, ImageType.Banner);
|
||||||
|
|
||||||
// Thumb
|
// Thumb
|
||||||
AddImage(files, images, imagePrefix + "thumb", ImageType.Thumb);
|
AddImage(files, images, "thumb", imagePrefix, isInMixedFolder, ImageType.Thumb);
|
||||||
AddImage(files, images, imagePrefix + "landscape", ImageType.Thumb);
|
AddImage(files, images, "landscape", imagePrefix, isInMixedFolder, ImageType.Thumb);
|
||||||
|
|
||||||
|
PopulateBackdrops(item, images, files, imagePrefix, isInMixedFolder, directoryService);
|
||||||
|
PopulateScreenshots(images, files, imagePrefix, isInMixedFolder);
|
||||||
|
|
||||||
if (supportParentSeriesFiles)
|
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);
|
var names = new List<string>
|
||||||
AddImage(files, images, imagePrefix + "cover", ImageType.Primary);
|
{
|
||||||
AddImage(files, images, imagePrefix + "poster", ImageType.Primary);
|
"folder",
|
||||||
AddImage(files, images, imagePrefix + "default", ImageType.Primary);
|
"cover",
|
||||||
|
"poster",
|
||||||
|
"default"
|
||||||
|
};
|
||||||
|
|
||||||
// Support plex/xbmc convention
|
// Support plex/kodi convention
|
||||||
if (item is Series)
|
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))
|
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, 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))
|
if (!string.IsNullOrEmpty(item.Path))
|
||||||
{
|
{
|
||||||
var name = _fileSystem.GetFileNameWithoutExtension(item.Path);
|
var name = item.FileNameWithoutExtension;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(name))
|
if (!string.IsNullOrEmpty(name))
|
||||||
{
|
{
|
||||||
AddImage(files, images, imagePrefix + name + "-fanart", ImageType.Backdrop);
|
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, "fanart", "fanart-", isInMixedFolder, ImageType.Backdrop);
|
||||||
PopulateBackdrops(images, files, imagePrefix, "background", "background-", ImageType.Backdrop);
|
PopulateBackdrops(images, files, imagePrefix, "background", "background-", isInMixedFolder, ImageType.Backdrop);
|
||||||
PopulateBackdrops(images, files, imagePrefix, "art", "art-", ImageType.Backdrop);
|
PopulateBackdrops(images, files, imagePrefix, "art", "art-", isInMixedFolder, ImageType.Backdrop);
|
||||||
|
|
||||||
var extraFanartFolder = files
|
var extraFanartFolder = files
|
||||||
.FirstOrDefault(i => string.Equals(i.Name, "extrafanart", StringComparison.OrdinalIgnoreCase));
|
.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);
|
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");
|
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)
|
private bool AddImage(IEnumerable<FileSystemInfo> files, List<LocalImageInfo> images, string name, ImageType type)
|
||||||
{
|
{
|
||||||
var image = GetImage(files, name) as FileInfo;
|
var image = GetImage(files, name) as FileInfo;
|
||||||
|
|
|
@ -72,7 +72,6 @@
|
||||||
<Compile Include="Providers\PlaylistXmlProvider.cs" />
|
<Compile Include="Providers\PlaylistXmlProvider.cs" />
|
||||||
<Compile Include="Providers\SeasonXmlProvider.cs" />
|
<Compile Include="Providers\SeasonXmlProvider.cs" />
|
||||||
<Compile Include="Providers\SeriesXmlProvider.cs" />
|
<Compile Include="Providers\SeriesXmlProvider.cs" />
|
||||||
<Compile Include="Providers\TrailerXmlProvider.cs" />
|
|
||||||
<Compile Include="Providers\VideoXmlProvider.cs" />
|
<Compile Include="Providers\VideoXmlProvider.cs" />
|
||||||
<Compile Include="Savers\BoxSetXmlSaver.cs" />
|
<Compile Include="Savers\BoxSetXmlSaver.cs" />
|
||||||
<Compile Include="Savers\ChannelXmlSaver.cs" />
|
<Compile Include="Savers\ChannelXmlSaver.cs" />
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
using MediaBrowser.Controller.Playlists;
|
using MediaBrowser.Controller.Playlists;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
namespace MediaBrowser.LocalMetadata.Parsers
|
namespace MediaBrowser.LocalMetadata.Parsers
|
||||||
|
@ -20,7 +22,15 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
||||||
{
|
{
|
||||||
case "OwnerUserId":
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
using System.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using System.Threading;
|
|
||||||
using MediaBrowser.Common.IO;
|
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.LocalMetadata.Parsers;
|
using MediaBrowser.LocalMetadata.Parsers;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace MediaBrowser.LocalMetadata.Providers
|
namespace MediaBrowser.LocalMetadata.Providers
|
||||||
{
|
{
|
||||||
|
@ -25,22 +25,10 @@ namespace MediaBrowser.LocalMetadata.Providers
|
||||||
|
|
||||||
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
|
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
|
||||||
{
|
{
|
||||||
var fileInfo = FileSystem.GetFileSystemInfo(info.Path);
|
var specificFile = Path.ChangeExtension(info.Path, ".xml");
|
||||||
|
|
||||||
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 file = new FileInfo(specificFile);
|
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>");
|
builder.Append("<Item>");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(playlist.OwnerUserId))
|
|
||||||
{
|
|
||||||
builder.Append("<OwnerUserId>" + SecurityElement.Escape(playlist.OwnerUserId) + "</OwnerUserId>");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(playlist.PlaylistMediaType))
|
if (!string.IsNullOrEmpty(playlist.PlaylistMediaType))
|
||||||
{
|
{
|
||||||
builder.Append("<PlaylistMediaType>" + SecurityElement.Escape(playlist.PlaylistMediaType) + "</PlaylistMediaType>");
|
builder.Append("<PlaylistMediaType>" + SecurityElement.Escape(playlist.PlaylistMediaType) + "</PlaylistMediaType>");
|
||||||
|
|
|
@ -645,6 +645,29 @@ namespace MediaBrowser.LocalMetadata.Savers
|
||||||
{
|
{
|
||||||
AddLinkedChildren(playlist, builder, "PlaylistItems", "PlaylistItem");
|
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)
|
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.
|
// 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) :
|
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);
|
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">
|
<Compile Include="..\MediaBrowser.Model\Devices\DeviceOptions.cs">
|
||||||
<Link>Devices\DeviceOptions.cs</Link>
|
<Link>Devices\DeviceOptions.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\Devices\DeviceQuery.cs">
|
||||||
|
<Link>Devices\DeviceQuery.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Devices\DevicesOptions.cs">
|
<Compile Include="..\MediaBrowser.Model\Devices\DevicesOptions.cs">
|
||||||
<Link>Devices\DevicesOptions.cs</Link>
|
<Link>Devices\DevicesOptions.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -347,9 +350,6 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Dlna\ProfileConditionValue.cs">
|
<Compile Include="..\MediaBrowser.Model\Dlna\ProfileConditionValue.cs">
|
||||||
<Link>Dlna\ProfileConditionValue.cs</Link>
|
<Link>Dlna\ProfileConditionValue.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Dlna\Profiles\AndroidProfile.cs">
|
|
||||||
<Link>Dlna\Profiles\AndroidProfile.cs</Link>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="..\MediaBrowser.Model\Dlna\Profiles\DefaultProfile.cs">
|
<Compile Include="..\MediaBrowser.Model\Dlna\Profiles\DefaultProfile.cs">
|
||||||
<Link>Dlna\Profiles\DefaultProfile.cs</Link>
|
<Link>Dlna\Profiles\DefaultProfile.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -404,12 +404,12 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Drawing\DrawingUtils.cs">
|
<Compile Include="..\MediaBrowser.Model\Drawing\DrawingUtils.cs">
|
||||||
<Link>Drawing\DrawingUtils.cs</Link>
|
<Link>Drawing\DrawingUtils.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\Drawing\ImageFormat.cs">
|
||||||
|
<Link>Drawing\ImageFormat.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Drawing\ImageOrientation.cs">
|
<Compile Include="..\MediaBrowser.Model\Drawing\ImageOrientation.cs">
|
||||||
<Link>Drawing\ImageOrientation.cs</Link>
|
<Link>Drawing\ImageOrientation.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Drawing\ImageOutputFormat.cs">
|
|
||||||
<Link>Drawing\ImageOutputFormat.cs</Link>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="..\MediaBrowser.Model\Drawing\ImageSize.cs">
|
<Compile Include="..\MediaBrowser.Model\Drawing\ImageSize.cs">
|
||||||
<Link>Drawing\ImageSize.cs</Link>
|
<Link>Drawing\ImageSize.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -422,6 +422,9 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Dto\ChapterInfoDto.cs">
|
<Compile Include="..\MediaBrowser.Model\Dto\ChapterInfoDto.cs">
|
||||||
<Link>Dto\ChapterInfoDto.cs</Link>
|
<Link>Dto\ChapterInfoDto.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\Dto\DtoOptions.cs">
|
||||||
|
<Link>Dto\DtoOptions.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Dto\GameSystemSummary.cs">
|
<Compile Include="..\MediaBrowser.Model\Dto\GameSystemSummary.cs">
|
||||||
<Link>Dto\GameSystemSummary.cs</Link>
|
<Link>Dto\GameSystemSummary.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -1031,6 +1034,12 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobItem.cs">
|
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobItem.cs">
|
||||||
<Link>Sync\SyncJobItem.cs</Link>
|
<Link>Sync\SyncJobItem.cs</Link>
|
||||||
</Compile>
|
</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">
|
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobQuery.cs">
|
||||||
<Link>Sync\SyncJobQuery.cs</Link>
|
<Link>Sync\SyncJobQuery.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -1040,9 +1049,6 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobStatus.cs">
|
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobStatus.cs">
|
||||||
<Link>Sync\SyncJobStatus.cs</Link>
|
<Link>Sync\SyncJobStatus.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Sync\SyncLimitType.cs">
|
|
||||||
<Link>Sync\SyncLimitType.cs</Link>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="..\MediaBrowser.Model\Sync\SyncQuality.cs">
|
<Compile Include="..\MediaBrowser.Model\Sync\SyncQuality.cs">
|
||||||
<Link>Sync\SyncQuality.cs</Link>
|
<Link>Sync\SyncQuality.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -1118,6 +1124,9 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Users\PinRedeemResult.cs">
|
<Compile Include="..\MediaBrowser.Model\Users\PinRedeemResult.cs">
|
||||||
<Link>Users\PinRedeemResult.cs</Link>
|
<Link>Users\PinRedeemResult.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\Users\UserPolicy.cs">
|
||||||
|
<Link>Users\UserPolicy.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\SharedVersion.cs">
|
<Compile Include="..\SharedVersion.cs">
|
||||||
<Link>Properties\SharedVersion.cs</Link>
|
<Link>Properties\SharedVersion.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
|
@ -240,6 +240,9 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Devices\DeviceOptions.cs">
|
<Compile Include="..\MediaBrowser.Model\Devices\DeviceOptions.cs">
|
||||||
<Link>Devices\DeviceOptions.cs</Link>
|
<Link>Devices\DeviceOptions.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\Devices\DeviceQuery.cs">
|
||||||
|
<Link>Devices\DeviceQuery.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Devices\DevicesOptions.cs">
|
<Compile Include="..\MediaBrowser.Model\Devices\DevicesOptions.cs">
|
||||||
<Link>Devices\DevicesOptions.cs</Link>
|
<Link>Devices\DevicesOptions.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -312,9 +315,6 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Dlna\ProfileConditionValue.cs">
|
<Compile Include="..\MediaBrowser.Model\Dlna\ProfileConditionValue.cs">
|
||||||
<Link>Dlna\ProfileConditionValue.cs</Link>
|
<Link>Dlna\ProfileConditionValue.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Dlna\Profiles\AndroidProfile.cs">
|
|
||||||
<Link>Dlna\Profiles\AndroidProfile.cs</Link>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="..\MediaBrowser.Model\Dlna\Profiles\DefaultProfile.cs">
|
<Compile Include="..\MediaBrowser.Model\Dlna\Profiles\DefaultProfile.cs">
|
||||||
<Link>Dlna\Profiles\DefaultProfile.cs</Link>
|
<Link>Dlna\Profiles\DefaultProfile.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -369,12 +369,12 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Drawing\DrawingUtils.cs">
|
<Compile Include="..\MediaBrowser.Model\Drawing\DrawingUtils.cs">
|
||||||
<Link>Drawing\DrawingUtils.cs</Link>
|
<Link>Drawing\DrawingUtils.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\Drawing\ImageFormat.cs">
|
||||||
|
<Link>Drawing\ImageFormat.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Drawing\ImageOrientation.cs">
|
<Compile Include="..\MediaBrowser.Model\Drawing\ImageOrientation.cs">
|
||||||
<Link>Drawing\ImageOrientation.cs</Link>
|
<Link>Drawing\ImageOrientation.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Drawing\ImageOutputFormat.cs">
|
|
||||||
<Link>Drawing\ImageOutputFormat.cs</Link>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="..\MediaBrowser.Model\Drawing\ImageSize.cs">
|
<Compile Include="..\MediaBrowser.Model\Drawing\ImageSize.cs">
|
||||||
<Link>Drawing\ImageSize.cs</Link>
|
<Link>Drawing\ImageSize.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -387,6 +387,9 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Dto\ChapterInfoDto.cs">
|
<Compile Include="..\MediaBrowser.Model\Dto\ChapterInfoDto.cs">
|
||||||
<Link>Dto\ChapterInfoDto.cs</Link>
|
<Link>Dto\ChapterInfoDto.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\Dto\DtoOptions.cs">
|
||||||
|
<Link>Dto\DtoOptions.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Dto\GameSystemSummary.cs">
|
<Compile Include="..\MediaBrowser.Model\Dto\GameSystemSummary.cs">
|
||||||
<Link>Dto\GameSystemSummary.cs</Link>
|
<Link>Dto\GameSystemSummary.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -990,6 +993,12 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobItem.cs">
|
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobItem.cs">
|
||||||
<Link>Sync\SyncJobItem.cs</Link>
|
<Link>Sync\SyncJobItem.cs</Link>
|
||||||
</Compile>
|
</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">
|
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobQuery.cs">
|
||||||
<Link>Sync\SyncJobQuery.cs</Link>
|
<Link>Sync\SyncJobQuery.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -999,9 +1008,6 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobStatus.cs">
|
<Compile Include="..\MediaBrowser.Model\Sync\SyncJobStatus.cs">
|
||||||
<Link>Sync\SyncJobStatus.cs</Link>
|
<Link>Sync\SyncJobStatus.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Sync\SyncLimitType.cs">
|
|
||||||
<Link>Sync\SyncLimitType.cs</Link>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="..\MediaBrowser.Model\Sync\SyncQuality.cs">
|
<Compile Include="..\MediaBrowser.Model\Sync\SyncQuality.cs">
|
||||||
<Link>Sync\SyncQuality.cs</Link>
|
<Link>Sync\SyncQuality.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -1077,6 +1083,9 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Users\PinRedeemResult.cs">
|
<Compile Include="..\MediaBrowser.Model\Users\PinRedeemResult.cs">
|
||||||
<Link>Users\PinRedeemResult.cs</Link>
|
<Link>Users\PinRedeemResult.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\Users\UserPolicy.cs">
|
||||||
|
<Link>Users\UserPolicy.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\SharedVersion.cs">
|
<Compile Include="..\SharedVersion.cs">
|
||||||
<Link>Properties\SharedVersion.cs</Link>
|
<Link>Properties\SharedVersion.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
|
@ -3,6 +3,7 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
public enum ConnectionMode
|
public enum ConnectionMode
|
||||||
{
|
{
|
||||||
Local = 1,
|
Local = 1,
|
||||||
Remote = 2
|
Remote = 2,
|
||||||
|
Manual = 3
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,6 +14,7 @@ using MediaBrowser.Model.Querying;
|
||||||
using MediaBrowser.Model.Search;
|
using MediaBrowser.Model.Search;
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
using MediaBrowser.Model.Session;
|
using MediaBrowser.Model.Session;
|
||||||
|
using MediaBrowser.Model.Sync;
|
||||||
using MediaBrowser.Model.System;
|
using MediaBrowser.Model.System;
|
||||||
using MediaBrowser.Model.Tasks;
|
using MediaBrowser.Model.Tasks;
|
||||||
using MediaBrowser.Model.Users;
|
using MediaBrowser.Model.Users;
|
||||||
|
@ -413,7 +414,7 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="query">The query.</param>
|
/// <param name="query">The query.</param>
|
||||||
/// <returns>Task{ItemsResult}.</returns>
|
/// <returns>Task{ItemsResult}.</returns>
|
||||||
Task<ItemsResult> GetUpcomingEpisodesAsync(NextUpQuery query);
|
Task<ItemsResult> GetUpcomingEpisodesAsync(UpcomingEpisodesQuery query);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a genre
|
/// Gets a genre
|
||||||
|
@ -1371,6 +1372,20 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
/// <returns>Task<DevicesOptions>.</returns>
|
/// <returns>Task<DevicesOptions>.</returns>
|
||||||
Task<DevicesOptions> GetDevicesOptions();
|
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<SyncJob>.</returns>
|
||||||
|
Task<SyncJob> RequestSync(SyncJobRequest request);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Opens the web socket.
|
/// Opens the web socket.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -50,10 +50,14 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
{
|
{
|
||||||
existing.RemoteAddress = server.RemoteAddress;
|
existing.RemoteAddress = server.RemoteAddress;
|
||||||
}
|
}
|
||||||
if (!existing.IsLocalAddressFixed && !string.IsNullOrEmpty(server.LocalAddress))
|
if (!string.IsNullOrEmpty(server.LocalAddress))
|
||||||
{
|
{
|
||||||
existing.LocalAddress = server.LocalAddress;
|
existing.LocalAddress = server.LocalAddress;
|
||||||
}
|
}
|
||||||
|
if (!string.IsNullOrEmpty(server.ManualAddress))
|
||||||
|
{
|
||||||
|
existing.LocalAddress = server.ManualAddress;
|
||||||
|
}
|
||||||
if (!string.IsNullOrEmpty(server.Name))
|
if (!string.IsNullOrEmpty(server.Name))
|
||||||
{
|
{
|
||||||
existing.Name = server.Name;
|
existing.Name = server.Name;
|
||||||
|
@ -62,9 +66,9 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
{
|
{
|
||||||
existing.WakeOnLanInfos = server.WakeOnLanInfos.ToList();
|
existing.WakeOnLanInfos = server.WakeOnLanInfos.ToList();
|
||||||
}
|
}
|
||||||
if (server.IsLocalAddressFixed)
|
if (server.LastConnectionMode.HasValue)
|
||||||
{
|
{
|
||||||
existing.IsLocalAddressFixed = true;
|
existing.LastConnectionMode = server.LastConnectionMode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -18,5 +18,10 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The name.</value>
|
/// <value>The name.</value>
|
||||||
public string Name { get; set; }
|
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 Id { get; set; }
|
||||||
public String LocalAddress { get; set; }
|
public String LocalAddress { get; set; }
|
||||||
public String RemoteAddress { get; set; }
|
public String RemoteAddress { get; set; }
|
||||||
|
public String ManualAddress { get; set; }
|
||||||
public String UserId { get; set; }
|
public String UserId { get; set; }
|
||||||
public String AccessToken { get; set; }
|
public String AccessToken { get; set; }
|
||||||
public List<WakeOnLanInfo> WakeOnLanInfos { get; set; }
|
public List<WakeOnLanInfo> WakeOnLanInfos { get; set; }
|
||||||
public DateTime DateLastAccessed { get; set; }
|
public DateTime DateLastAccessed { get; set; }
|
||||||
public String ExchangeToken { get; set; }
|
public String ExchangeToken { get; set; }
|
||||||
public UserLinkType? UserLinkType { get; set; }
|
public UserLinkType? UserLinkType { get; set; }
|
||||||
|
public ConnectionMode? LastConnectionMode { get; set; }
|
||||||
public bool IsLocalAddressFixed { get; set; }
|
|
||||||
|
|
||||||
public ServerInfo()
|
public ServerInfo()
|
||||||
{
|
{
|
||||||
|
@ -30,7 +30,7 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
Name = systemInfo.ServerName;
|
Name = systemInfo.ServerName;
|
||||||
Id = systemInfo.Id;
|
Id = systemInfo.Id;
|
||||||
|
|
||||||
if (!IsLocalAddressFixed && !string.IsNullOrEmpty(systemInfo.LocalAddress))
|
if (!string.IsNullOrEmpty(systemInfo.LocalAddress))
|
||||||
{
|
{
|
||||||
LocalAddress = 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,12 @@ namespace MediaBrowser.Model.Configuration
|
||||||
/// <value><c>true</c> if [save local meta]; otherwise, <c>false</c>.</value>
|
/// <value><c>true</c> if [save local meta]; otherwise, <c>false</c>.</value>
|
||||||
public bool SaveLocalMeta { get; set; }
|
public bool SaveLocalMeta { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether [enable localized guids].
|
||||||
|
/// </summary>
|
||||||
|
/// <value><c>true</c> if [enable localized guids]; otherwise, <c>false</c>.</value>
|
||||||
|
public bool EnableLocalizedGuids { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the preferred metadata language.
|
/// Gets or sets the preferred metadata language.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
17
MediaBrowser.Model/Devices/DeviceQuery.cs
Normal file
17
MediaBrowser.Model/Devices/DeviceQuery.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
namespace MediaBrowser.Model.Devices
|
||||||
|
{
|
||||||
|
public class DeviceQuery
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether [supports content uploading].
|
||||||
|
/// </summary>
|
||||||
|
/// <value><c>null</c> if [supports content uploading] contains no value, <c>true</c> if [supports content uploading]; otherwise, <c>false</c>.</value>
|
||||||
|
public bool? SupportsContentUploading { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether [supports unique identifier].
|
||||||
|
/// </summary>
|
||||||
|
/// <value><c>null</c> if [supports unique identifier] contains no value, <c>true</c> if [supports unique identifier]; otherwise, <c>false</c>.</value>
|
||||||
|
public bool? SupportsUniqueIdentifier { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,167 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Xml.Serialization;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Dlna.Profiles
|
|
||||||
{
|
|
||||||
[XmlRoot("Profile")]
|
|
||||||
public class AndroidProfile : DefaultProfile
|
|
||||||
{
|
|
||||||
public AndroidProfile()
|
|
||||||
: this(true, true, new[] { "baseline", "constrained baseline" })
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public AndroidProfile(bool supportsHls,
|
|
||||||
bool supportsMpegDash,
|
|
||||||
string[] supportedH264Profiles)
|
|
||||||
{
|
|
||||||
Name = "Android";
|
|
||||||
|
|
||||||
List<TranscodingProfile> transcodingProfiles = new List<TranscodingProfile>();
|
|
||||||
|
|
||||||
transcodingProfiles.Add(new TranscodingProfile
|
|
||||||
{
|
|
||||||
Container = "mp3",
|
|
||||||
AudioCodec = "mp3",
|
|
||||||
Type = DlnaProfileType.Audio
|
|
||||||
});
|
|
||||||
|
|
||||||
if (supportsMpegDash)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
if (supportsHls)
|
|
||||||
{
|
|
||||||
transcodingProfiles.Add(new TranscodingProfile
|
|
||||||
{
|
|
||||||
Protocol = "hls",
|
|
||||||
Container = "ts",
|
|
||||||
VideoCodec = "h264",
|
|
||||||
AudioCodec = "aac",
|
|
||||||
Type = DlnaProfileType.Video,
|
|
||||||
Context = EncodingContext.Streaming
|
|
||||||
});
|
|
||||||
}
|
|
||||||
transcodingProfiles.Add(new TranscodingProfile
|
|
||||||
{
|
|
||||||
Container = "mp4",
|
|
||||||
VideoCodec = "h264",
|
|
||||||
AudioCodec = "aac",
|
|
||||||
Type = DlnaProfileType.Video,
|
|
||||||
Context = EncodingContext.Static
|
|
||||||
});
|
|
||||||
|
|
||||||
TranscodingProfiles = transcodingProfiles.ToArray();
|
|
||||||
|
|
||||||
DirectPlayProfiles = new[]
|
|
||||||
{
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "mp4",
|
|
||||||
VideoCodec = "h264,mpeg4",
|
|
||||||
AudioCodec = "aac",
|
|
||||||
Type = DlnaProfileType.Video
|
|
||||||
},
|
|
||||||
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "mp4,aac",
|
|
||||||
AudioCodec = "aac",
|
|
||||||
Type = DlnaProfileType.Audio
|
|
||||||
},
|
|
||||||
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "mp3",
|
|
||||||
AudioCodec = "mp3",
|
|
||||||
Type = DlnaProfileType.Audio
|
|
||||||
},
|
|
||||||
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "flac",
|
|
||||||
AudioCodec = "flac",
|
|
||||||
Type = DlnaProfileType.Audio
|
|
||||||
},
|
|
||||||
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "ogg",
|
|
||||||
AudioCodec = "vorbis",
|
|
||||||
Type = DlnaProfileType.Audio
|
|
||||||
},
|
|
||||||
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "jpeg,png,gif,bmp",
|
|
||||||
Type = DlnaProfileType.Photo
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
CodecProfiles = new[]
|
|
||||||
{
|
|
||||||
new CodecProfile
|
|
||||||
{
|
|
||||||
Type = CodecType.Video,
|
|
||||||
Codec= "h264",
|
|
||||||
|
|
||||||
Conditions = new []
|
|
||||||
{
|
|
||||||
new ProfileCondition(ProfileConditionType.EqualsAny, ProfileConditionValue.VideoProfile, string.Join("|", supportedH264Profiles)),
|
|
||||||
new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.Width, "1920"),
|
|
||||||
new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.Height, "1080"),
|
|
||||||
new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.VideoBitDepth, "8"),
|
|
||||||
new ProfileCondition(ProfileConditionType.NotEquals, ProfileConditionValue.IsAnamorphic, "true"),
|
|
||||||
new ProfileCondition(ProfileConditionType.Equals, ProfileConditionValue.IsCabac, "true")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new CodecProfile
|
|
||||||
{
|
|
||||||
Type = CodecType.Video,
|
|
||||||
|
|
||||||
Conditions = new []
|
|
||||||
{
|
|
||||||
new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.Width, "1920"),
|
|
||||||
new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.Height, "1080"),
|
|
||||||
new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.VideoBitDepth, "8"),
|
|
||||||
new ProfileCondition(ProfileConditionType.NotEquals, ProfileConditionValue.IsAnamorphic, "true")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new CodecProfile
|
|
||||||
{
|
|
||||||
Type = CodecType.VideoAudio,
|
|
||||||
Codec = "aac",
|
|
||||||
Conditions = new []
|
|
||||||
{
|
|
||||||
new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.AudioChannels, "2")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new CodecProfile
|
|
||||||
{
|
|
||||||
Type = CodecType.Audio,
|
|
||||||
Codec = "aac",
|
|
||||||
Conditions = new []
|
|
||||||
{
|
|
||||||
new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.AudioChannels, "2")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new CodecProfile
|
|
||||||
{
|
|
||||||
Type = CodecType.Audio,
|
|
||||||
Codec = "mp3",
|
|
||||||
Conditions = new []
|
|
||||||
{
|
|
||||||
new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.AudioChannels, "2"),
|
|
||||||
new ProfileCondition(ProfileConditionType.LessThanEqual, ProfileConditionValue.AudioBitrate, "320000")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue