add connect to startup wizard

pull/702/head
Luke Pulverenti 10 years ago
parent 813f5d9649
commit 3be4aa8dc7

@ -172,19 +172,8 @@ namespace MediaBrowser.Api
if (!string.IsNullOrWhiteSpace(deviceId)) if (!string.IsNullOrWhiteSpace(deviceId))
{ {
var audioCodec = state.Request.AudioCodec; var audioCodec = state.ActualOutputVideoCodec;
var videoCodec = state.VideoRequest == null ? null : state.VideoRequest.VideoCodec; var videoCodec = state.ActualOutputVideoCodec;
if (string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase) ||
string.IsNullOrEmpty(audioCodec))
{
audioCodec = state.OutputAudioCodec;
}
if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) ||
string.IsNullOrEmpty(videoCodec))
{
videoCodec = state.OutputVideoCodec;
}
_sessionManager.ReportTranscodingInfo(deviceId, new TranscodingInfo _sessionManager.ReportTranscodingInfo(deviceId, new TranscodingInfo
{ {

@ -172,13 +172,6 @@
<PostBuildEvent> <PostBuildEvent>
</PostBuildEvent> </PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- 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.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

@ -830,23 +830,6 @@ namespace MediaBrowser.Api.Playback
return MediaEncoder.GetInputArgument(inputPath, protocol); return MediaEncoder.GetInputArgument(inputPath, protocol);
} }
private MediaProtocol GetProtocol(string path)
{
if (path.StartsWith("Http", StringComparison.OrdinalIgnoreCase))
{
return MediaProtocol.Http;
}
if (path.StartsWith("Rtsp", StringComparison.OrdinalIgnoreCase))
{
return MediaProtocol.Rtsp;
}
if (path.StartsWith("Rtmp", StringComparison.OrdinalIgnoreCase))
{
return MediaProtocol.Rtmp;
}
return MediaProtocol.File;
}
private async Task AcquireResources(StreamState state, CancellationTokenSource cancellationTokenSource) private async Task AcquireResources(StreamState state, CancellationTokenSource cancellationTokenSource)
{ {
if (state.VideoType == VideoType.Iso && state.IsoType.HasValue && IsoManager.CanMount(state.MediaPath)) if (state.VideoType == VideoType.Iso && state.IsoType.HasValue && IsoManager.CanMount(state.MediaPath))
@ -1788,9 +1771,23 @@ namespace MediaBrowser.Api.Playback
} }
// If client is requesting a specific video profile, it must match the source // If client is requesting a specific video profile, it must match the source
if (!string.IsNullOrEmpty(request.Profile) && !string.Equals(request.Profile, videoStream.Profile, StringComparison.OrdinalIgnoreCase)) if (!string.IsNullOrEmpty(request.Profile))
{ {
return false; if (string.IsNullOrEmpty(videoStream.Profile))
{
return false;
}
if (!string.Equals(request.Profile, videoStream.Profile, StringComparison.OrdinalIgnoreCase))
{
var currentScore = GetVideoProfileScore(videoStream.Profile);
var requestedScore = GetVideoProfileScore(request.Profile);
if (currentScore == -1 || currentScore > requestedScore)
{
return false;
}
}
} }
// Video width must fall within requested value // Video width must fall within requested value
@ -1870,6 +1867,22 @@ namespace MediaBrowser.Api.Playback
return request.EnableAutoStreamCopy; return request.EnableAutoStreamCopy;
} }
private int GetVideoProfileScore(string profile)
{
var list = new List<string>
{
"Constrained Baseline",
"Baseline",
"Extended",
"Main",
"High",
"Progressive High",
"Constrained High"
};
return Array.FindIndex(list.ToArray(), t => string.Equals(t, profile, StringComparison.OrdinalIgnoreCase));
}
private bool CanStreamCopyAudio(VideoStreamRequest request, MediaStream audioStream, List<string> supportedAudioCodecs) private bool CanStreamCopyAudio(VideoStreamRequest request, MediaStream audioStream, List<string> supportedAudioCodecs)
{ {
// Source and target codecs must match // Source and target codecs must match
@ -1942,19 +1955,9 @@ namespace MediaBrowser.Api.Playback
return; return;
} }
var audioCodec = state.OutputAudioCodec; var audioCodec = state.ActualOutputAudioCodec;
if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.AudioStream != null)
{
audioCodec = state.AudioStream.Codec;
}
var videoCodec = state.OutputVideoCodec;
if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.VideoStream != null) var videoCodec = state.ActualOutputVideoCodec;
{
videoCodec = state.VideoStream.Codec;
}
var mediaProfile = state.VideoRequest == null ? var mediaProfile = state.VideoRequest == null ?
profile.GetAudioMediaProfile(state.OutputContainer, audioCodec, state.OutputAudioChannels, state.OutputAudioBitrate) : profile.GetAudioMediaProfile(state.OutputContainer, audioCodec, state.OutputAudioChannels, state.OutputAudioBitrate) :
@ -2022,12 +2025,7 @@ namespace MediaBrowser.Api.Playback
profile = DlnaManager.GetDefaultProfile(); profile = DlnaManager.GetDefaultProfile();
} }
var audioCodec = state.OutputAudioCodec; var audioCodec = state.ActualOutputAudioCodec;
if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.AudioStream != null)
{
audioCodec = state.AudioStream.Codec;
}
if (state.VideoRequest == null) if (state.VideoRequest == null)
{ {
@ -2045,12 +2043,7 @@ namespace MediaBrowser.Api.Playback
} }
else else
{ {
var videoCodec = state.OutputVideoCodec; var videoCodec = state.ActualOutputVideoCodec;
if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.VideoStream != null)
{
videoCodec = state.VideoStream.Codec;
}
responseHeaders["contentFeatures.dlna.org"] = new ContentFeatureBuilder(profile) responseHeaders["contentFeatures.dlna.org"] = new ContentFeatureBuilder(profile)
.BuildVideoHeader( .BuildVideoHeader(

@ -605,7 +605,7 @@ namespace MediaBrowser.Api.Playback.Hls
{ {
var codec = state.OutputAudioCodec; var codec = state.OutputAudioCodec;
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
{ {
return "-codec:a:0 copy"; return "-codec:a:0 copy";
} }
@ -636,7 +636,7 @@ namespace MediaBrowser.Api.Playback.Hls
var codec = state.OutputVideoCodec; var codec = state.OutputVideoCodec;
// See if we can save come cpu cycles by avoiding encoding // See if we can save come cpu cycles by avoiding encoding
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
{ {
return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf:v h264_mp4toannexb" : "-codec:v:0 copy"; return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf:v h264_mp4toannexb" : "-codec:v:0 copy";
} }

@ -188,6 +188,50 @@ namespace MediaBrowser.Api.Playback
public int? OutputAudioBitrate; public int? OutputAudioBitrate;
public int? OutputVideoBitrate; public int? OutputVideoBitrate;
public string ActualOutputVideoCodec
{
get
{
var codec = OutputVideoCodec;
if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
{
var stream = VideoStream;
if (stream != null)
{
return stream.Codec;
}
return null;
}
return codec;
}
}
public string ActualOutputAudioCodec
{
get
{
var codec = OutputAudioCodec;
if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
{
var stream = AudioStream;
if (stream != null)
{
return stream.Codec;
}
return null;
}
return codec;
}
}
public string OutputContainer { get; set; } public string OutputContainer { get; set; }
public DeviceProfile DeviceProfile { get; set; } public DeviceProfile DeviceProfile { get; set; }

@ -29,6 +29,9 @@ namespace MediaBrowser.Api
[ApiMember(Name = "IsDisabled", Description = "Optional filter by IsDisabled=true or false", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "IsDisabled", Description = "Optional filter by IsDisabled=true or false", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsDisabled { get; set; } public bool? IsDisabled { get; set; }
[ApiMember(Name = "IsGuest", Description = "Optional filter by IsGuest=true or false", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsGuest { get; set; }
} }
[Route("/Users/Public", "GET", Summary = "Gets a list of publicly visible users for display on a login screen.")] [Route("/Users/Public", "GET", Summary = "Gets a list of publicly visible users for display on a login screen.")]
@ -255,6 +258,12 @@ namespace MediaBrowser.Api
users = users.Where(i => i.Configuration.IsHidden == request.IsHidden.Value); users = users.Where(i => i.Configuration.IsHidden == request.IsHidden.Value);
} }
if (request.IsGuest.HasValue)
{
users = users.Where(i => (i.ConnectLinkType.HasValue && i.ConnectLinkType.Value == UserLinkType.Guest) == request.IsGuest.Value);
}
var result = users var result = users
.OrderBy(u => u.Name) .OrderBy(u => u.Name)
.Select(i => _userManager.GetUserDto(i, Request.RemoteIp)) .Select(i => _userManager.GetUserDto(i, Request.RemoteIp))

@ -124,13 +124,6 @@
xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\" /y /d /r /i xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\" /y /d /r /i
)</PostBuildEvent> )</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- 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.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

@ -74,6 +74,12 @@ namespace MediaBrowser.Controller.Entities
} }
} }
[IgnoreDataMember]
public virtual bool AlwaysScanInternalMetadataPath
{
get { return false; }
}
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is in mixed folder. /// Gets a value indicating whether this instance is in mixed folder.
/// </summary> /// </summary>

@ -172,6 +172,12 @@ namespace MediaBrowser.Controller.Entities
/// </summary> /// </summary>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
string GetInternalMetadataPath(); string GetInternalMetadataPath();
/// <summary>
/// Gets a value indicating whether [always scan internal metadata path].
/// </summary>
/// <value><c>true</c> if [always scan internal metadata path]; otherwise, <c>false</c>.</value>
bool AlwaysScanInternalMetadataPath { get; }
} }
public static class HasImagesExtensions public static class HasImagesExtensions

@ -1,5 +1,6 @@
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using System.Linq; using System.Linq;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities
{ {
@ -13,6 +14,15 @@ namespace MediaBrowser.Controller.Entities
} }
} }
[IgnoreDataMember]
public override bool AlwaysScanInternalMetadataPath
{
get
{
return true;
}
}
protected override bool GetBlockUnratedValue(UserConfiguration config) protected override bool GetBlockUnratedValue(UserConfiguration config)
{ {
return config.BlockUnratedItems.Contains(UnratedItem.Other); return config.BlockUnratedItems.Contains(UnratedItem.Other);

@ -369,13 +369,6 @@ xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\" /y /d /r /i
<PreBuildEvent> <PreBuildEvent>
</PreBuildEvent> </PreBuildEvent>
</PropertyGroup> </PropertyGroup>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- 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.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

@ -15,6 +15,7 @@ namespace MediaBrowser.Controller.Playlists
{ {
public string OwnerUserId { get; set; } public string OwnerUserId { get; set; }
[IgnoreDataMember]
protected override bool FilterLinkedChildrenPerUser protected override bool FilterLinkedChildrenPerUser
{ {
get get
@ -23,6 +24,15 @@ namespace MediaBrowser.Controller.Playlists
} }
} }
[IgnoreDataMember]
public override bool AlwaysScanInternalMetadataPath
{
get
{
return true;
}
}
public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren) public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)
{ {
return GetPlayableItems(user); return GetPlayableItems(user);

@ -49,6 +49,19 @@ namespace MediaBrowser.Controller.Providers
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken); Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken);
/// <summary>
/// Saves the image.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="source">The source.</param>
/// <param name="mimeType">Type of the MIME.</param>
/// <param name="type">The type.</param>
/// <param name="imageIndex">Index of the image.</param>
/// <param name="internalCacheKey">The internal cache key.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Adds the metadata providers. /// Adds the metadata providers.
/// </summary> /// </summary>

@ -37,7 +37,7 @@ namespace MediaBrowser.LocalMetadata.Images
{ {
var collectionFolder = (CollectionFolder)item; var collectionFolder = (CollectionFolder)item;
return new LocalImageProvider(_fileSystem).GetImages(item, collectionFolder.PhysicalLocations, directoryService); return new LocalImageProvider(_fileSystem).GetImages(item, collectionFolder.PhysicalLocations, false, directoryService);
} }
} }
} }

@ -45,7 +45,7 @@ namespace MediaBrowser.LocalMetadata.Images
try try
{ {
return new LocalImageProvider(_fileSystem).GetImages(item, path, directoryService); return new LocalImageProvider(_fileSystem).GetImages(item, path, false, directoryService);
} }
catch (DirectoryNotFoundException) catch (DirectoryNotFoundException)
{ {

@ -38,7 +38,7 @@ namespace MediaBrowser.LocalMetadata.Images
return true; return true;
} }
if (item.SupportsLocalMetadata) if (item.SupportsLocalMetadata && !item.AlwaysScanInternalMetadataPath)
{ {
return false; return false;
} }
@ -59,14 +59,9 @@ namespace MediaBrowser.LocalMetadata.Images
{ {
var path = item.GetInternalMetadataPath(); var path = item.GetInternalMetadataPath();
if (item is IChannelItem)
{
var b = true;
}
try try
{ {
return new LocalImageProvider(_fileSystem).GetImages(item, path, directoryService); return new LocalImageProvider(_fileSystem).GetImages(item, path, true, directoryService);
} }
catch (DirectoryNotFoundException) catch (DirectoryNotFoundException)
{ {

@ -21,7 +21,7 @@ namespace MediaBrowser.LocalMetadata.Images
{ {
_fileSystem = fileSystem; _fileSystem = fileSystem;
} }
public string Name public string Name
{ {
get { return "Local Images"; } get { return "Local Images"; }
@ -94,12 +94,12 @@ namespace MediaBrowser.LocalMetadata.Images
return list; return list;
} }
public List<LocalImageInfo> GetImages(IHasImages item, string path, IDirectoryService directoryService) public List<LocalImageInfo> GetImages(IHasImages item, string path, bool checkForCacheKeyFiles, IDirectoryService directoryService)
{ {
return GetImages(item, new[] { path }, directoryService); return GetImages(item, new[] { path }, checkForCacheKeyFiles, directoryService);
} }
public List<LocalImageInfo> GetImages(IHasImages item, IEnumerable<string> paths, IDirectoryService directoryService) public List<LocalImageInfo> GetImages(IHasImages item, IEnumerable<string> paths, bool checkForCacheKeyFiles, IDirectoryService directoryService)
{ {
var files = paths.SelectMany(directoryService.GetFiles) var files = paths.SelectMany(directoryService.GetFiles)
.Where(i => .Where(i =>
@ -115,6 +115,12 @@ namespace MediaBrowser.LocalMetadata.Images
PopulateImages(item, list, files, false, directoryService); PopulateImages(item, list, files, false, directoryService);
if (checkForCacheKeyFiles)
{
AddCacheKeyImage(files, list, ImageType.Primary);
AddCacheKeyImage(files, list, ImageType.Thumb);
}
return list; return list;
} }
@ -322,6 +328,26 @@ namespace MediaBrowser.LocalMetadata.Images
return false; return false;
} }
private void AddCacheKeyImage(IEnumerable<FileSystemInfo> files, List<LocalImageInfo> images, ImageType type)
{
var candidates = files
.Where(i => _fileSystem.GetFileNameWithoutExtension(i).StartsWith(type.ToString() + "_key_", StringComparison.OrdinalIgnoreCase))
.ToList();
var image = BaseItem.SupportedImageExtensions
.Select(i => candidates.FirstOrDefault(c => string.Equals(c.Extension, i, StringComparison.OrdinalIgnoreCase)))
.FirstOrDefault(i => i != null) as FileInfo;
if (image != null)
{
images.Add(new LocalImageInfo
{
FileInfo = image,
Type = type
});
}
}
private FileSystemInfo GetImage(IEnumerable<FileSystemInfo> files, string name) private FileSystemInfo GetImage(IEnumerable<FileSystemInfo> files, string name)
{ {
var candidates = files var candidates = files

@ -91,13 +91,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- 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.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

@ -1118,13 +1118,6 @@ xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\portable\" /y /d /r /i
)</PostBuildEvent> )</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<Import Project="Fody.targets" /> <Import Project="Fody.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- 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.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

@ -1,4 +1,5 @@
using System.Collections.Generic; using MediaBrowser.Model.Connect;
using System.Collections.Generic;
namespace MediaBrowser.Model.ApiClient namespace MediaBrowser.Model.ApiClient
{ {
@ -7,6 +8,7 @@ namespace MediaBrowser.Model.ApiClient
public ConnectionState State { get; set; } public ConnectionState State { get; set; }
public List<ServerInfo> Servers { get; set; } public List<ServerInfo> Servers { get; set; }
public IApiClient ApiClient { get; set; } public IApiClient ApiClient { get; set; }
public ConnectUser ConnectUser { get; set; }
public ConnectionResult() public ConnectionResult()
{ {

@ -1,8 +1,8 @@
using System.Collections.Generic; using MediaBrowser.Model.Connect;
using MediaBrowser.Model.Connect;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Events; using MediaBrowser.Model.Events;
using System; using System;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -14,12 +14,29 @@ namespace MediaBrowser.Model.ApiClient
/// Occurs when [connected]. /// Occurs when [connected].
/// </summary> /// </summary>
event EventHandler<GenericEventArgs<ConnectionResult>> Connected; event EventHandler<GenericEventArgs<ConnectionResult>> Connected;
/// <summary>
/// Occurs when [local user sign in].
/// </summary>
event EventHandler<GenericEventArgs<UserDto>> LocalUserSignIn;
/// <summary>
/// Occurs when [connect user sign in].
/// </summary>
event EventHandler<GenericEventArgs<ConnectUser>> ConnectUserSignIn;
/// <summary>
/// Occurs when [local user sign out].
/// </summary>
event EventHandler<EventArgs> LocalUserSignOut;
/// <summary>
/// Occurs when [connect user sign out].
/// </summary>
event EventHandler<EventArgs> ConnectUserSignOut;
/// <summary> /// <summary>
/// Occurs when [remote logged out]. /// Gets the connect user.
/// </summary> /// </summary>
event EventHandler<EventArgs> RemoteLoggedOut; /// <value>The connect user.</value>
ConnectUser ConnectUser { get; }
/// <summary> /// <summary>
/// Gets the API client. /// Gets the API client.
/// </summary> /// </summary>

@ -238,6 +238,103 @@ namespace MediaBrowser.Model.Configuration
{ {
new MetadataOptions(1, 1280) {ItemType = "Book"}, new MetadataOptions(1, 1280) {ItemType = "Book"},
new MetadataOptions(1, 1280)
{
ItemType = "Movie",
ImageOptions = new []
{
new ImageOption
{
Limit = 3,
MinWidth = 1280,
Type = ImageType.Backdrop
},
// Don't download this by default as it's rarely used.
new ImageOption
{
Limit = 0,
Type = ImageType.Art
},
// Don't download this by default as it's rarely used.
new ImageOption
{
Limit = 0,
Type = ImageType.Disc
},
new ImageOption
{
Limit = 1,
Type = ImageType.Primary
},
new ImageOption
{
Limit = 1,
Type = ImageType.Banner
},
new ImageOption
{
Limit = 1,
Type = ImageType.Thumb
},
new ImageOption
{
Limit = 1,
Type = ImageType.Logo
}
}
},
new MetadataOptions(1, 1280)
{
ItemType = "Series",
ImageOptions = new []
{
new ImageOption
{
Limit = 2,
MinWidth = 1280,
Type = ImageType.Backdrop
},
// Don't download this by default as it's rarely used.
new ImageOption
{
Limit = 0,
Type = ImageType.Art
},
new ImageOption
{
Limit = 1,
Type = ImageType.Primary
},
new ImageOption
{
Limit = 1,
Type = ImageType.Banner
},
new ImageOption
{
Limit = 1,
Type = ImageType.Thumb
},
new ImageOption
{
Limit = 1,
Type = ImageType.Logo
}
}
},
new MetadataOptions(1, 1280) new MetadataOptions(1, 1280)
{ {
ItemType = "MusicAlbum", ItemType = "MusicAlbum",

@ -674,7 +674,7 @@ namespace MediaBrowser.Model.Dlna
} }
case ProfileConditionValue.VideoProfile: case ProfileConditionValue.VideoProfile:
{ {
item.VideoProfile = value; item.VideoProfile = (value ?? string.Empty).Split('|')[0];
break; break;
} }
case ProfileConditionValue.Height: case ProfileConditionValue.Height:

@ -426,13 +426,6 @@ xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\net45\" /y /d /r /i
)</PostBuildEvent> )</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<Import Project="Fody.targets" /> <Import Project="Fody.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- 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.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

@ -1,6 +1,5 @@
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers; using MediaBrowser.Model.Providers;
@ -33,12 +32,6 @@ namespace MediaBrowser.Providers.FolderImages
public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken) public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{ {
var playlist = item as Playlist;
if (playlist != null)
{
return GetImages(string.Empty, cancellationToken);
}
var view = item as UserView; var view = item as UserView;
if (view != null) if (view != null)
@ -119,7 +112,7 @@ namespace MediaBrowser.Providers.FolderImages
public bool Supports(IHasImages item) public bool Supports(IHasImages item)
{ {
return item is UserView || item is ICollectionFolder || item is Playlist; return item is UserView || item is ICollectionFolder;
} }
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)

@ -1,5 +1,6 @@
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO; using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
@ -63,7 +64,12 @@ namespace MediaBrowser.Providers.Manager
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException">mimeType</exception> /// <exception cref="System.ArgumentNullException">mimeType</exception>
public async Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken) public Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken)
{
return SaveImage(item, source, mimeType, type, imageIndex, null, cancellationToken);
}
public async Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken)
{ {
if (string.IsNullOrEmpty(mimeType)) if (string.IsNullOrEmpty(mimeType))
{ {
@ -108,6 +114,10 @@ namespace MediaBrowser.Providers.Manager
} }
} }
} }
if (!string.IsNullOrEmpty(internalCacheKey))
{
saveLocally = false;
}
if (!imageIndex.HasValue && item.AllowsMultipleImages(type)) if (!imageIndex.HasValue && item.AllowsMultipleImages(type))
{ {
@ -116,7 +126,9 @@ namespace MediaBrowser.Providers.Manager
var index = imageIndex ?? 0; var index = imageIndex ?? 0;
var paths = GetSavePaths(item, type, imageIndex, mimeType, saveLocally); var paths = !string.IsNullOrEmpty(internalCacheKey) ?
new[] { GetCacheKeyPath(item, type, mimeType, internalCacheKey) } :
GetSavePaths(item, type, imageIndex, mimeType, saveLocally);
// If there are more than one output paths, the stream will need to be seekable // If there are more than one output paths, the stream will need to be seekable
if (paths.Length > 1 && !source.CanSeek) if (paths.Length > 1 && !source.CanSeek)
@ -180,6 +192,12 @@ namespace MediaBrowser.Providers.Manager
} }
} }
private string GetCacheKeyPath(IHasImages item, ImageType type, string mimeType, string key)
{
var extension = MimeTypes.ToExtension(mimeType);
return Path.Combine(item.GetInternalMetadataPath(), type.ToString().ToLower() + "_key_" + key + extension);
}
/// <summary> /// <summary>
/// Saves the image to location. /// Saves the image to location.
/// </summary> /// </summary>
@ -300,7 +318,7 @@ namespace MediaBrowser.Providers.Manager
private string GetStandardSavePath(IHasImages item, ImageType type, int? imageIndex, string mimeType, bool saveLocally) private string GetStandardSavePath(IHasImages item, ImageType type, int? imageIndex, string mimeType, bool saveLocally)
{ {
string filename; string filename;
switch (type) switch (type)
{ {
case ImageType.Art: case ImageType.Art:
@ -399,13 +417,7 @@ namespace MediaBrowser.Providers.Manager
{ {
var season = item as Season; var season = item as Season;
var extension = mimeType.Split('/').Last(); var extension = MimeTypes.ToExtension(mimeType);
if (string.Equals(extension, "jpeg", StringComparison.OrdinalIgnoreCase))
{
extension = "jpg";
}
extension = "." + extension.ToLower();
// Backdrop paths // Backdrop paths
if (type == ImageType.Backdrop) if (type == ImageType.Backdrop)

@ -137,6 +137,11 @@ namespace MediaBrowser.Providers.Manager
return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, cancellationToken); return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, cancellationToken);
} }
public Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken)
{
return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, internalCacheKey, cancellationToken);
}
public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(IHasImages item, RemoteImageQuery query, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(IHasImages item, RemoteImageQuery query, CancellationToken cancellationToken)
{ {
var providers = GetRemoteImageProviders(item, query.IncludeDisabledProviders); var providers = GetRemoteImageProviders(item, query.IncludeDisabledProviders);

@ -149,6 +149,7 @@
<Compile Include="Music\MusicBrainzAlbumProvider.cs" /> <Compile Include="Music\MusicBrainzAlbumProvider.cs" />
<Compile Include="People\PersonMetadataService.cs" /> <Compile Include="People\PersonMetadataService.cs" />
<Compile Include="People\MovieDbPersonProvider.cs" /> <Compile Include="People\MovieDbPersonProvider.cs" />
<Compile Include="Photos\PhotoAlbumMetadataService.cs" />
<Compile Include="Photos\PhotoHelper.cs" /> <Compile Include="Photos\PhotoHelper.cs" />
<Compile Include="Photos\PhotoMetadataService.cs" /> <Compile Include="Photos\PhotoMetadataService.cs" />
<Compile Include="Photos\PhotoProvider.cs" /> <Compile Include="Photos\PhotoProvider.cs" />
@ -215,13 +216,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- 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.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

@ -0,0 +1,33 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
namespace MediaBrowser.Providers.Photos
{
class PhotoAlbumMetadataService : MetadataService<PhotoAlbum, ItemLookupInfo>
{
public PhotoAlbumMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, IUserDataManager userDataManager)
: base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem, userDataManager)
{
}
/// <summary>
/// Merges the specified source.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="target">The target.</param>
/// <param name="lockedFields">The locked fields.</param>
/// <param name="replaceData">if set to <c>true</c> [replace data].</param>
/// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
protected override void MergeData(PhotoAlbum source, PhotoAlbum target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
}
}

@ -1,6 +1,4 @@
using System.Security.Cryptography; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;

@ -159,7 +159,12 @@ namespace MediaBrowser.Server.Implementations.Library
throw new ArgumentNullException("username"); throw new ArgumentNullException("username");
} }
var user = Users.First(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase)); var user = Users.FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
if (user == null)
{
throw new AuthenticationException("Invalid username or password entered.");
}
if (user.Configuration.IsDisabled) if (user.Configuration.IsDisabled)
{ {

@ -472,7 +472,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var item = _itemRepo.RetrieveItem(id) as LiveTvChannel; var item = _itemRepo.RetrieveItem(id) as LiveTvChannel;
if (item == null) if (item == null || !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase))
{ {
item = new LiveTvChannel item = new LiveTvChannel
{ {

@ -36,10 +36,10 @@
"LabelMovie": "Film", "LabelMovie": "Film",
"LabelMusicVideo": "Video Musicali", "LabelMusicVideo": "Video Musicali",
"LabelEpisode": "Episodio", "LabelEpisode": "Episodio",
"LabelSeries": "Series", "LabelSeries": "Serie TV",
"LabelStopping": "Sto fermando", "LabelStopping": "Sto fermando",
"LabelCancelled": "(cancellato)", "LabelCancelled": "(cancellato)",
"LabelFailed": "(failed)", "LabelFailed": "(fallito)",
"LabelAbortedByServerShutdown": "(Interrotto dalla chiusura del server)", "LabelAbortedByServerShutdown": "(Interrotto dalla chiusura del server)",
"LabelScheduledTaskLastRan": "Last ran {0}, taking {1}.", "LabelScheduledTaskLastRan": "Last ran {0}, taking {1}.",
"HeaderDeleteTaskTrigger": "Elimina Operazione pianificata", "HeaderDeleteTaskTrigger": "Elimina Operazione pianificata",
@ -310,8 +310,8 @@
"TabAdvanced": "Avanzato", "TabAdvanced": "Avanzato",
"TabHelp": "Aiuto", "TabHelp": "Aiuto",
"TabScheduledTasks": "Operazioni pianificate", "TabScheduledTasks": "Operazioni pianificate",
"ButtonFullscreen": "Fullscreen", "ButtonFullscreen": "Schermo intero",
"ButtonAudioTracks": "Audio Tracks", "ButtonAudioTracks": "Tracce audio",
"ButtonSubtitles": "Sottotitoli", "ButtonSubtitles": "Sottotitoli",
"ButtonScenes": "Scene", "ButtonScenes": "Scene",
"ButtonQuality": "Qualit\u00e0", "ButtonQuality": "Qualit\u00e0",
@ -383,7 +383,7 @@
"PersonTypePerson": "Persona", "PersonTypePerson": "Persona",
"LabelTitleDisplayOrder": "Titolo mostrato in ordine:", "LabelTitleDisplayOrder": "Titolo mostrato in ordine:",
"OptionSortName": "Nome ordinato", "OptionSortName": "Nome ordinato",
"OptionReleaseDate": "Release date", "OptionReleaseDate": "Data di uscita",
"LabelSeasonNumber": "Numero Stagione:", "LabelSeasonNumber": "Numero Stagione:",
"LabelDiscNumber": "Disco numero", "LabelDiscNumber": "Disco numero",
"LabelParentNumber": "Numero superiore", "LabelParentNumber": "Numero superiore",
@ -525,7 +525,7 @@
"HeaderAlbums": "Album", "HeaderAlbums": "Album",
"HeaderGames": "Giochi", "HeaderGames": "Giochi",
"HeaderBooks": "Libri", "HeaderBooks": "Libri",
"HeaderEpisodes": "Episodes", "HeaderEpisodes": "Episodi",
"HeaderSeasons": "Stagioni", "HeaderSeasons": "Stagioni",
"HeaderTracks": "Traccia", "HeaderTracks": "Traccia",
"HeaderItems": "Elementi", "HeaderItems": "Elementi",
@ -594,8 +594,8 @@
"DashboardTourNotifications": "Inviare automaticamente notifiche di eventi server al vostro dispositivo mobile, e-mail e altro ancora.", "DashboardTourNotifications": "Inviare automaticamente notifiche di eventi server al vostro dispositivo mobile, e-mail e altro ancora.",
"DashboardTourScheduledTasks": "Gestire facilmente le operazioni di lunga esecuzione con le operazioni pianificate. Decidere quando corrono, e con quale frequenza.", "DashboardTourScheduledTasks": "Gestire facilmente le operazioni di lunga esecuzione con le operazioni pianificate. Decidere quando corrono, e con quale frequenza.",
"DashboardTourMobile": "Il cruscotto Media Browser funziona alla grande su smartphone e tablet. Gestisci il tuo server dal palmo della tua mano in qualsiasi momento, ovunque.", "DashboardTourMobile": "Il cruscotto Media Browser funziona alla grande su smartphone e tablet. Gestisci il tuo server dal palmo della tua mano in qualsiasi momento, ovunque.",
"MessageRefreshQueued": "Refresh queued", "MessageRefreshQueued": "Aggiornamento programmato",
"TabDevices": "Devices", "TabDevices": "Dispositivi",
"DeviceLastUsedByUserName": "Last used by {0}", "DeviceLastUsedByUserName": "Last used by {0}",
"HeaderDeleteDevice": "Delete Device", "HeaderDeleteDevice": "Delete Device",
"DeleteDeviceConfirmation": "Are you sure you wish to delete this device? It will reappear the next time a user signs in with it.", "DeleteDeviceConfirmation": "Are you sure you wish to delete this device? It will reappear the next time a user signs in with it.",

@ -601,6 +601,6 @@
"DeleteDeviceConfirmation": "\u0428\u044b\u043d\u044b\u043c\u0435\u043d \u043e\u0441\u044b \u0436\u0430\u0431\u0434\u044b\u049b\u0442\u044b \u0436\u043e\u044e \u049b\u0430\u0436\u0435\u0442 \u043f\u0435? \u0411\u04b1\u043b \u043a\u0435\u043b\u0435\u0441\u0456 \u0440\u0435\u0442\u0442\u0435 \u043f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b \u043e\u0441\u044b\u0434\u0430\u043d \u043a\u0456\u0440\u0433\u0435\u043d\u0434\u0435 \u049b\u0430\u0439\u0442\u0430 \u043f\u0430\u0439\u0434\u0430 \u0431\u043e\u043b\u0430\u0434\u044b.", "DeleteDeviceConfirmation": "\u0428\u044b\u043d\u044b\u043c\u0435\u043d \u043e\u0441\u044b \u0436\u0430\u0431\u0434\u044b\u049b\u0442\u044b \u0436\u043e\u044e \u049b\u0430\u0436\u0435\u0442 \u043f\u0435? \u0411\u04b1\u043b \u043a\u0435\u043b\u0435\u0441\u0456 \u0440\u0435\u0442\u0442\u0435 \u043f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b \u043e\u0441\u044b\u0434\u0430\u043d \u043a\u0456\u0440\u0433\u0435\u043d\u0434\u0435 \u049b\u0430\u0439\u0442\u0430 \u043f\u0430\u0439\u0434\u0430 \u0431\u043e\u043b\u0430\u0434\u044b.",
"LabelEnableCameraUploadFor": "\u041c\u044b\u043d\u0430\u0443 \u04af\u0448\u0456\u043d \u043a\u0430\u043c\u0435\u0440\u0430\u0434\u0430\u043d \u043a\u0435\u0440\u0456 \u049b\u043e\u0442\u0430\u0440\u0443:", "LabelEnableCameraUploadFor": "\u041c\u044b\u043d\u0430\u0443 \u04af\u0448\u0456\u043d \u043a\u0430\u043c\u0435\u0440\u0430\u0434\u0430\u043d \u043a\u0435\u0440\u0456 \u049b\u043e\u0442\u0430\u0440\u0443:",
"HeaderSelectUploadPath": "\u041a\u0435\u0440\u0456 \u049b\u043e\u0442\u0430\u0440\u0443 \u0436\u043e\u043b\u044b\u043d \u0442\u0430\u04a3\u0434\u0430\u0443", "HeaderSelectUploadPath": "\u041a\u0435\u0440\u0456 \u049b\u043e\u0442\u0430\u0440\u0443 \u0436\u043e\u043b\u044b\u043d \u0442\u0430\u04a3\u0434\u0430\u0443",
"LabelEnableCameraUploadForHelp": "Uploads will occur automatically in the background when signed into Media Browser.", "LabelEnableCameraUploadForHelp": "Media Browser \u0456\u0448\u0456\u043d\u0435 \u043a\u0456\u0440\u0433\u0435\u043d\u0434\u0435 \u043a\u0435\u0440\u0456 \u049b\u043e\u0442\u0430\u0440\u0443\u043b\u0430\u0440 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0442\u044b \u0442\u04af\u0440\u0434\u0435 \u04e9\u043d\u0434\u0456\u043a \u0440\u0435\u0436\u0456\u043c\u0456\u043d\u0434\u0435 \u04e9\u0442\u0435\u0434\u0456.",
"ErrorMessageStartHourGreaterThanEnd": "\u0410\u044f\u049b\u0442\u0430\u0443 \u0443\u0430\u049b\u044b\u0442\u044b \u0431\u0430\u0441\u0442\u0430\u0443 \u0443\u0430\u049b\u044b\u0442\u044b\u043d\u0430\u043d \u043a\u0435\u0439\u0456\u043d\u0440\u0435\u043a \u0431\u043e\u043b\u0443\u044b \u049b\u0430\u0436\u0435\u0442 \u0435\u0442\u0435\u0434\u0456." "ErrorMessageStartHourGreaterThanEnd": "\u0410\u044f\u049b\u0442\u0430\u0443 \u0443\u0430\u049b\u044b\u0442\u044b \u0431\u0430\u0441\u0442\u0430\u0443 \u0443\u0430\u049b\u044b\u0442\u044b\u043d\u0430\u043d \u043a\u0435\u0439\u0456\u043d\u0440\u0435\u043a \u0431\u043e\u043b\u0443\u044b \u049b\u0430\u0436\u0435\u0442 \u0435\u0442\u0435\u0434\u0456."
} }

@ -36,10 +36,10 @@
"LabelMovie": "Filme", "LabelMovie": "Filme",
"LabelMusicVideo": "V\u00eddeo Musical", "LabelMusicVideo": "V\u00eddeo Musical",
"LabelEpisode": "Epis\u00f3dio", "LabelEpisode": "Epis\u00f3dio",
"LabelSeries": "Series", "LabelSeries": "S\u00e9rie",
"LabelStopping": "Parando", "LabelStopping": "Parando",
"LabelCancelled": "(cancelado)", "LabelCancelled": "(cancelado)",
"LabelFailed": "(failed)", "LabelFailed": "(falhou)",
"LabelAbortedByServerShutdown": "(Abortada pelo desligamento do servidor)", "LabelAbortedByServerShutdown": "(Abortada pelo desligamento do servidor)",
"LabelScheduledTaskLastRan": "\u00daltima execu\u00e7\u00e3o {0}, demorando {1}.", "LabelScheduledTaskLastRan": "\u00daltima execu\u00e7\u00e3o {0}, demorando {1}.",
"HeaderDeleteTaskTrigger": "Excluir Disparador da Tarefa", "HeaderDeleteTaskTrigger": "Excluir Disparador da Tarefa",
@ -63,7 +63,7 @@
"ButtonPlay": "Reproduzir", "ButtonPlay": "Reproduzir",
"ButtonEdit": "Editar", "ButtonEdit": "Editar",
"ButtonQueue": "Adicionar \u00e0 fila", "ButtonQueue": "Adicionar \u00e0 fila",
"ButtonPlayTrailer": "Play trailer", "ButtonPlayTrailer": "Reproduzir trailer",
"ButtonPlaylist": "Lista de reprodu\u00e7\u00e3o", "ButtonPlaylist": "Lista de reprodu\u00e7\u00e3o",
"ButtonPreviousTrack": "Faixa Anterior", "ButtonPreviousTrack": "Faixa Anterior",
"LabelEnabled": "Ativada", "LabelEnabled": "Ativada",
@ -310,8 +310,8 @@
"TabAdvanced": "Avan\u00e7ado", "TabAdvanced": "Avan\u00e7ado",
"TabHelp": "Ajuda", "TabHelp": "Ajuda",
"TabScheduledTasks": "Tarefas Agendadas", "TabScheduledTasks": "Tarefas Agendadas",
"ButtonFullscreen": "Fullscreen", "ButtonFullscreen": "Tela cheia",
"ButtonAudioTracks": "Audio Tracks", "ButtonAudioTracks": "Faixas de \u00c1udio",
"ButtonSubtitles": "Legendas", "ButtonSubtitles": "Legendas",
"ButtonScenes": "Cenas", "ButtonScenes": "Cenas",
"ButtonQuality": "Qualidade", "ButtonQuality": "Qualidade",
@ -383,7 +383,7 @@
"PersonTypePerson": "Pessoa", "PersonTypePerson": "Pessoa",
"LabelTitleDisplayOrder": "Ordem de exibi\u00e7\u00e3o do t\u00edtulo: ", "LabelTitleDisplayOrder": "Ordem de exibi\u00e7\u00e3o do t\u00edtulo: ",
"OptionSortName": "Nome para ordena\u00e7\u00e3o", "OptionSortName": "Nome para ordena\u00e7\u00e3o",
"OptionReleaseDate": "Release date", "OptionReleaseDate": "Data de lan\u00e7amento",
"LabelSeasonNumber": "N\u00famero da temporada:", "LabelSeasonNumber": "N\u00famero da temporada:",
"LabelDiscNumber": "N\u00famero do disco", "LabelDiscNumber": "N\u00famero do disco",
"LabelParentNumber": "N\u00famero do superior", "LabelParentNumber": "N\u00famero do superior",
@ -525,7 +525,7 @@
"HeaderAlbums": "\u00c1lbuns", "HeaderAlbums": "\u00c1lbuns",
"HeaderGames": "Jogos", "HeaderGames": "Jogos",
"HeaderBooks": "Livros", "HeaderBooks": "Livros",
"HeaderEpisodes": "Episodes", "HeaderEpisodes": "Epis\u00f3dios",
"HeaderSeasons": "Temporadas", "HeaderSeasons": "Temporadas",
"HeaderTracks": "Faixas", "HeaderTracks": "Faixas",
"HeaderItems": "Itens", "HeaderItems": "Itens",
@ -602,5 +602,5 @@
"LabelEnableCameraUploadFor": "Habilitar envio atrav\u00e9s de c\u00e2mera para:", "LabelEnableCameraUploadFor": "Habilitar envio atrav\u00e9s de c\u00e2mera para:",
"HeaderSelectUploadPath": "Selecione o caminho para carga", "HeaderSelectUploadPath": "Selecione o caminho para carga",
"LabelEnableCameraUploadForHelp": "Cargas ser\u00e3o executadas automaticamente em retaguarda quando logar no Media Browser.", "LabelEnableCameraUploadForHelp": "Cargas ser\u00e3o executadas automaticamente em retaguarda quando logar no Media Browser.",
"ErrorMessageStartHourGreaterThanEnd": "End time must be greater than the start time." "ErrorMessageStartHourGreaterThanEnd": "O tempo final deve ser maior que o tempo inicial."
} }

@ -0,0 +1,606 @@
{
"SettingsSaved": "\u8bbe\u7f6e\u5df2\u4fdd\u5b58",
"AddUser": "\u6dfb\u52a0\u7528\u6237",
"Users": "\u7528\u6237",
"Delete": "\u5220\u9664",
"Administrator": "\u7ba1\u7406\u5458",
"Password": "\u5bc6\u7801",
"DeleteImage": "\u5220\u9664\u56fe\u50cf",
"DeleteImageConfirmation": "\u4f60\u786e\u5b9a\u8981\u5220\u9664\u6b64\u56fe\u50cf\uff1f",
"FileReadCancelled": "\u6587\u4ef6\u8bfb\u53d6\u5df2\u88ab\u53d6\u6d88\u3002",
"FileNotFound": "\u672a\u627e\u5230\u6587\u4ef6\u3002",
"FileReadError": "\u8bfb\u53d6\u6587\u4ef6\u53d1\u751f\u9519\u8bef\u3002",
"DeleteUser": "\u5220\u9664\u7528\u6237",
"DeleteUserConfirmation": "\u4f60\u786e\u5b9a\u8981\u5220\u9664\u6b64\u7528\u6237\uff1f",
"PasswordResetHeader": "\u5bc6\u7801\u91cd\u7f6e",
"PasswordResetComplete": "\u5bc6\u7801\u5df2\u91cd\u7f6e",
"PasswordResetConfirmation": "\u4f60\u786e\u5b9a\u8981\u91cd\u7f6e\u5bc6\u7801\uff1f",
"PasswordSaved": "\u5bc6\u7801\u5df2\u4fdd\u5b58\u3002",
"PasswordMatchError": "\u5bc6\u7801\u548c\u786e\u8ba4\u5bc6\u7801\u5fc5\u987b\u5339\u914d\u3002",
"OptionRelease": "\u5b98\u65b9\u6b63\u5f0f\u7248",
"OptionBeta": "\u6d4b\u8bd5\u7248",
"OptionDev": "\u5f00\u53d1\u7248\uff08\u4e0d\u7a33\u5b9a\uff09",
"UninstallPluginHeader": "\u5378\u8f7d\u63d2\u4ef6",
"UninstallPluginConfirmation": "\u4f60\u786e\u5b9a\u8981\u5378\u8f7d {0}?",
"NoPluginConfigurationMessage": "\u6b64\u63d2\u4ef6\u6ca1\u6709\u914d\u7f6e\u9009\u9879\u3002",
"NoPluginsInstalledMessage": "\u4f60\u6ca1\u6709\u5b89\u88c5\u63d2\u4ef6\u3002",
"BrowsePluginCatalogMessage": "\u6d4f\u89c8\u6211\u4eec\u7684\u63d2\u4ef6\u76ee\u5f55\u6765\u67e5\u770b\u73b0\u6709\u63d2\u4ef6\u3002",
"MessageKeyEmailedTo": "Key emailed to {0}.",
"MessageKeysLinked": "Keys linked.",
"HeaderConfirmation": "Confirmation",
"MessageKeyUpdated": "Thank you. Your supporter key has been updated.",
"MessageKeyRemoved": "Thank you. Your supporter key has been removed.",
"ErrorLaunchingChromecast": "There was an error launching chromecast. Please ensure your device is connected to your wireless network.",
"HeaderSearch": "Search",
"LabelArtist": "Artist",
"LabelMovie": "Movie",
"LabelMusicVideo": "Music Video",
"LabelEpisode": "Episode",
"LabelSeries": "Series",
"LabelStopping": "Stopping",
"LabelCancelled": "(cancelled)",
"LabelFailed": "(failed)",
"LabelAbortedByServerShutdown": "(Aborted by server shutdown)",
"LabelScheduledTaskLastRan": "Last ran {0}, taking {1}.",
"HeaderDeleteTaskTrigger": "Delete Task Trigger",
"HeaderTaskTriggers": "Task Triggers",
"MessageDeleteTaskTrigger": "Are you sure you wish to delete this task trigger?",
"MessageNoPluginsInstalled": "You have no plugins installed.",
"LabelVersionInstalled": "{0} installed",
"LabelNumberReviews": "{0} Reviews",
"LabelFree": "Free",
"HeaderSelectAudio": "Select Audio",
"HeaderSelectSubtitles": "Select Subtitles",
"LabelDefaultStream": "(Default)",
"LabelForcedStream": "(Forced)",
"LabelDefaultForcedStream": "(Default\/Forced)",
"LabelUnknownLanguage": "Unknown language",
"ButtonMute": "Mute",
"ButtonUnmute": "Unmute",
"ButtonStop": "Stop",
"ButtonNextTrack": "Next Track",
"ButtonPause": "Pause",
"ButtonPlay": "Play",
"ButtonEdit": "Edit",
"ButtonQueue": "Queue",
"ButtonPlayTrailer": "Play trailer",
"ButtonPlaylist": "Playlist",
"ButtonPreviousTrack": "Previous Track",
"LabelEnabled": "Enabled",
"LabelDisabled": "Disabled",
"ButtonMoreInformation": "More Information",
"LabelNoUnreadNotifications": "No unread notifications.",
"ButtonViewNotifications": "View notifications",
"ButtonMarkTheseRead": "Mark these read",
"ButtonClose": "Close",
"LabelAllPlaysSentToPlayer": "All plays will be sent to the selected player.",
"MessageInvalidUser": "Invalid user or password.",
"HeaderAllRecordings": "All Recordings",
"RecommendationBecauseYouLike": "Because you like {0}",
"RecommendationBecauseYouWatched": "Because you watched {0}",
"RecommendationDirectedBy": "Directed by {0}",
"RecommendationStarring": "Starring {0}",
"HeaderConfirmRecordingCancellation": "Confirm Recording Cancellation",
"MessageConfirmRecordingCancellation": "Are you sure you wish to cancel this recording?",
"MessageRecordingCancelled": "Recording cancelled.",
"HeaderConfirmSeriesCancellation": "Confirm Series Cancellation",
"MessageConfirmSeriesCancellation": "Are you sure you wish to cancel this series?",
"MessageSeriesCancelled": "Series cancelled.",
"HeaderConfirmRecordingDeletion": "Confirm Recording Deletion",
"MessageConfirmRecordingDeletion": "Are you sure you wish to delete this recording?",
"MessageRecordingDeleted": "Recording deleted.",
"ButonCancelRecording": "Cancel Recording",
"MessageRecordingSaved": "Recording saved.",
"OptionSunday": "Sunday",
"OptionMonday": "Monday",
"OptionTuesday": "Tuesday",
"OptionWednesday": "Wednesday",
"OptionThursday": "Thursday",
"OptionFriday": "Friday",
"OptionSaturday": "Saturday",
"HeaderConfirmDeletion": "Confirm Deletion",
"MessageConfirmPathSubstitutionDeletion": "Are you sure you wish to delete this path substitution?",
"LiveTvUpdateAvailable": "(Update available)",
"LabelVersionUpToDate": "Up to date!",
"ButtonResetTuner": "Reset tuner",
"HeaderResetTuner": "Reset Tuner",
"MessageConfirmResetTuner": "Are you sure you wish to reset this tuner? Any active players or recordings will be abruptly stopped.",
"ButtonCancelSeries": "Cancel Series",
"HeaderSeriesRecordings": "Series Recordings",
"LabelAnytime": "Any time",
"StatusRecording": "Recording",
"StatusWatching": "Watching",
"StatusRecordingProgram": "Recording {0}",
"StatusWatchingProgram": "Watching {0}",
"HeaderSplitMedia": "Split Media Apart",
"MessageConfirmSplitMedia": "Are you sure you wish to split the media sources into separate items?",
"HeaderError": "Error",
"MessagePleaseSelectOneItem": "Please select at least one item.",
"MessagePleaseSelectTwoItems": "Please select at least two items.",
"MessageTheFollowingItemsWillBeGrouped": "The following titles will be grouped into one item:",
"MessageConfirmItemGrouping": "Media Browser clients will automatically choose the optimal version to play based on device and network performance. Are you sure you wish to continue?",
"HeaderResume": "Resume",
"HeaderMyViews": "My Views",
"HeaderLibraryFolders": "Media Folders",
"HeaderLatestMedia": "Latest Media",
"ButtonMoreItems": "More...",
"ButtonMore": "More",
"HeaderFavoriteMovies": "Favorite Movies",
"HeaderFavoriteShows": "Favorite Shows",
"HeaderFavoriteEpisodes": "Favorite Episodes",
"HeaderFavoriteGames": "Favorite Games",
"HeaderRatingsDownloads": "Rating \/ Downloads",
"HeaderConfirmProfileDeletion": "Confirm Profile Deletion",
"MessageConfirmProfileDeletion": "Are you sure you wish to delete this profile?",
"HeaderSelectServerCachePath": "Select Server Cache Path",
"HeaderSelectTranscodingPath": "Select Transcoding Temporary Path",
"HeaderSelectImagesByNamePath": "Select Images By Name Path",
"HeaderSelectMetadataPath": "Select Metadata Path",
"HeaderSelectServerCachePathHelp": "Browse or enter the path to use for server cache files. The folder must be writeable.",
"HeaderSelectTranscodingPathHelp": "Browse or enter the path to use for transcoding temporary files. The folder must be writeable.",
"HeaderSelectImagesByNamePathHelp": "Browse or enter the path to your items by name folder. The folder must be writeable.",
"HeaderSelectMetadataPathHelp": "Browse or enter the path you'd like to store metadata within. The folder must be writeable.",
"HeaderSelectChannelDownloadPath": "Select Channel Download Path",
"HeaderSelectChannelDownloadPathHelp": "Browse or enter the path to use for storing channel cache files. The folder must be writeable.",
"OptionNewCollection": "New...",
"ButtonAdd": "Add",
"ButtonRemove": "Remove",
"LabelChapterDownloaders": "Chapter downloaders:",
"LabelChapterDownloadersHelp": "Enable and rank your preferred chapter downloaders in order of priority. Lower priority downloaders will only be used to fill in missing information.",
"HeaderFavoriteAlbums": "Favorite Albums",
"HeaderLatestChannelMedia": "Latest Channel Items",
"ButtonOrganizeFile": "Organize File",
"ButtonDeleteFile": "Delete File",
"HeaderOrganizeFile": "Organize File",
"HeaderDeleteFile": "Delete File",
"StatusSkipped": "Skipped",
"StatusFailed": "Failed",
"StatusSuccess": "Success",
"MessageFileWillBeDeleted": "The following file will be deleted:",
"MessageSureYouWishToProceed": "Are you sure you wish to proceed?",
"MessageDuplicatesWillBeDeleted": "In addition the following dupliates will be deleted:",
"MessageFollowingFileWillBeMovedFrom": "The following file will be moved from:",
"MessageDestinationTo": "to:",
"HeaderSelectWatchFolder": "Select Watch Folder",
"HeaderSelectWatchFolderHelp": "Browse or enter the path to your watch folder. The folder must be writeable.",
"OrganizePatternResult": "Result: {0}",
"HeaderRestart": "Restart",
"HeaderShutdown": "Shutdown",
"MessageConfirmRestart": "Are you sure you wish to restart Media Browser Server?",
"MessageConfirmShutdown": "Are you sure you wish to shutdown Media Browser Server?",
"ButtonUpdateNow": "Update Now",
"NewVersionOfSomethingAvailable": "A new version of {0} is available!",
"VersionXIsAvailableForDownload": "Version {0} is now available for download.",
"LabelVersionNumber": "Version {0}",
"LabelPlayMethodTranscoding": "Transcoding",
"LabelPlayMethodDirectStream": "Direct Streaming",
"LabelPlayMethodDirectPlay": "Direct Playing",
"LabelAudioCodec": "Audio: {0}",
"LabelVideoCodec": "Video: {0}",
"LabelRemoteAccessUrl": "Remote access: {0}",
"LabelRunningOnPort": "Running on port {0}.",
"HeaderLatestFromChannel": "Latest from {0}",
"ButtonDownload": "Download",
"LabelUnknownLanaguage": "Unknown language",
"HeaderCurrentSubtitles": "Current Subtitles",
"MessageDownloadQueued": "The download has been queued.",
"MessageAreYouSureDeleteSubtitles": "Are you sure you wish to delete this subtitle file?",
"ButtonRemoteControl": "Remote Control",
"HeaderLatestTvRecordings": "Latest Recordings",
"ButtonOk": "Ok",
"ButtonCancel": "Cancel",
"ButtonRefresh": "Refresh",
"LabelCurrentPath": "Current path:",
"HeaderSelectMediaPath": "Select Media Path",
"ButtonNetwork": "Network",
"MessageDirectoryPickerInstruction": "Network paths can be entered manually in the event the Network button fails to locate your devices. For example, {0} or {1}.",
"HeaderMenu": "Menu",
"ButtonOpen": "Open",
"ButtonOpenInNewTab": "Open in new tab",
"ButtonShuffle": "Shuffle",
"ButtonInstantMix": "Instant mix",
"ButtonResume": "Resume",
"HeaderScenes": "Scenes",
"HeaderAudioTracks": "Audio Tracks",
"HeaderSubtitles": "Subtitles",
"HeaderVideoQuality": "Video Quality",
"MessageErrorPlayingVideo": "There was an error playing the video.",
"MessageEnsureOpenTuner": "Please ensure there is an open tuner availalble.",
"ButtonHome": "Home",
"ButtonDashboard": "Dashboard",
"ButtonReports": "Reports",
"ButtonMetadataManager": "Metadata Manager",
"HeaderTime": "Time",
"HeaderName": "Name",
"HeaderAlbum": "Album",
"HeaderAlbumArtist": "Album Artist",
"HeaderArtist": "Artist",
"LabelAddedOnDate": "Added {0}",
"ButtonStart": "Start",
"HeaderChannels": "Channels",
"HeaderMediaFolders": "Media Folders",
"HeaderBlockItemsWithNoRating": "Block items with no rating information:",
"OptionBlockOthers": "Others",
"OptionBlockTvShows": "TV Shows",
"OptionBlockTrailers": "Trailers",
"OptionBlockMusic": "Music",
"OptionBlockMovies": "Movies",
"OptionBlockBooks": "Books",
"OptionBlockGames": "Games",
"OptionBlockLiveTvPrograms": "Live TV Programs",
"OptionBlockLiveTvChannels": "Live TV Channels",
"OptionBlockChannelContent": "Internet Channel Content",
"ButtonRevoke": "Revoke",
"MessageConfirmRevokeApiKey": "Are you sure you wish to revoke this api key? The application's connection to Media Browser will be abruptly terminated.",
"HeaderConfirmRevokeApiKey": "Revoke Api Key",
"ValueContainer": "Container: {0}",
"ValueAudioCodec": "Audio Codec: {0}",
"ValueVideoCodec": "Video Codec: {0}",
"ValueCodec": "Codec: {0}",
"ValueConditions": "Conditions: {0}",
"LabelAll": "All",
"HeaderDeleteImage": "Delete Image",
"MessageFileNotFound": "File not found.",
"MessageFileReadError": "An error occurred reading this file.",
"ButtonNextPage": "Next Page",
"ButtonPreviousPage": "Previous Page",
"ButtonMoveLeft": "Move left",
"ButtonMoveRight": "Move right",
"ButtonBrowseOnlineImages": "Browse online images",
"HeaderDeleteItem": "Delete Item",
"ConfirmDeleteItem": "Are you sure you wish to delete this item from your library?",
"MessagePleaseEnterNameOrId": "Please enter a name or an external Id.",
"MessageValueNotCorrect": "The value entered is not correct. Please try again.",
"MessageItemSaved": "Item saved.",
"OptionEnded": "Ended",
"OptionContinuing": "Continuing",
"OptionOff": "Off",
"OptionOn": "On",
"HeaderFields": "Fields",
"HeaderFieldsHelp": "Slide a field to 'off' to lock it and prevent it's data from being changed.",
"HeaderLiveTV": "Live TV",
"MissingLocalTrailer": "Missing local trailer.",
"MissingPrimaryImage": "Missing primary image.",
"MissingBackdropImage": "Missing backdrop image.",
"MissingLogoImage": "Missing logo image.",
"MissingEpisode": "Missing episode.",
"OptionScreenshots": "Screenshots",
"OptionBackdrops": "Backdrops",
"OptionImages": "Images",
"OptionKeywords": "Keywords",
"OptionTags": "Tags",
"OptionStudios": "Studios",
"OptionName": "Name",
"OptionOverview": "Overview",
"OptionGenres": "Genres",
"OptionParentalRating": "Parental Rating",
"OptionPeople": "People",
"OptionRuntime": "Runtime",
"OptionProductionLocations": "Production Locations",
"OptionBirthLocation": "Birth Location",
"LabelAllChannels": "All channels",
"LabelLiveProgram": "LIVE",
"LabelNewProgram": "NEW",
"LabelPremiereProgram": "PREMIERE",
"LabelHDProgram": "HD",
"HeaderChangeFolderType": "Change Folder Type",
"HeaderChangeFolderTypeHelp": "To change the folder type, please remove and rebuild the collection with the new type.",
"HeaderAlert": "Alert",
"MessagePleaseRestart": "Please restart to finish updating.",
"ButtonRestart": "Restart",
"MessagePleaseRefreshPage": "Please refresh this page to receive new updates from the server.",
"ButtonHide": "Hide",
"MessageSettingsSaved": "Settings saved.",
"ButtonSignOut": "Sign Out",
"ButtonMyProfile": "My Profile",
"ButtonMyPreferences": "My Preferences",
"MessageBrowserDoesNotSupportWebSockets": "This browser does not support web sockets. For a better experience, try a newer browser such as Chrome, Firefox, IE10+, Safari (iOS) or Opera.",
"LabelInstallingPackage": "Installing {0}",
"LabelPackageInstallCompleted": "{0} installation completed.",
"LabelPackageInstallFailed": "{0} installation failed.",
"LabelPackageInstallCancelled": "{0} installation cancelled.",
"TabServer": "Server",
"TabUsers": "Users",
"TabLibrary": "Library",
"TabMetadata": "Metadata",
"TabDLNA": "DLNA",
"TabLiveTV": "Live TV",
"TabAutoOrganize": "Auto-Organize",
"TabPlugins": "Plugins",
"TabAdvanced": "Advanced",
"TabHelp": "Help",
"TabScheduledTasks": "Scheduled Tasks",
"ButtonFullscreen": "Fullscreen",
"ButtonAudioTracks": "Audio Tracks",
"ButtonSubtitles": "Subtitles",
"ButtonScenes": "Scenes",
"ButtonQuality": "Quality",
"HeaderNotifications": "Notifications",
"HeaderSelectPlayer": "Select Player:",
"ButtonSelect": "Select",
"ButtonNew": "New",
"MessageInternetExplorerWebm": "For best results with Internet Explorer please install the WebM playback plugin.",
"HeaderVideoError": "Video Error",
"ButtonAddToPlaylist": "Add to playlist",
"HeaderAddToPlaylist": "Add to Playlist",
"LabelName": "Name:",
"ButtonSubmit": "Submit",
"LabelSelectPlaylist": "Playlist:",
"OptionNewPlaylist": "New playlist...",
"MessageAddedToPlaylistSuccess": "Ok",
"ButtonView": "View",
"ButtonViewSeriesRecording": "View series recording",
"ValueOriginalAirDate": "Original air date: {0}",
"ButtonRemoveFromPlaylist": "Remove from playlist",
"HeaderSpecials": "Specials",
"HeaderTrailers": "Trailers",
"HeaderAudio": "Audio",
"HeaderResolution": "Resolution",
"HeaderVideo": "Video",
"HeaderRuntime": "Runtime",
"HeaderCommunityRating": "Community rating",
"HeaderParentalRating": "Parental rating",
"HeaderReleaseDate": "Release date",
"HeaderDateAdded": "Date added",
"HeaderSeries": "Series",
"HeaderSeason": "Season",
"HeaderSeasonNumber": "Season number",
"HeaderNetwork": "Network",
"HeaderYear": "Year",
"HeaderGameSystem": "Game system",
"HeaderPlayers": "Players",
"HeaderEmbeddedImage": "Embedded image",
"HeaderTrack": "Track",
"HeaderDisc": "Disc",
"OptionMovies": "Movies",
"OptionCollections": "Collections",
"OptionSeries": "Series",
"OptionSeasons": "Seasons",
"OptionEpisodes": "Episodes",
"OptionGames": "Games",
"OptionGameSystems": "Game systems",
"OptionMusicArtists": "Music artists",
"OptionMusicAlbums": "Music albums",
"OptionMusicVideos": "Music videos",
"OptionSongs": "Songs",
"OptionHomeVideos": "Home videos",
"OptionBooks": "Books",
"OptionAdultVideos": "Adult videos",
"ButtonUp": "Up",
"ButtonDown": "Down",
"LabelMetadataReaders": "Metadata readers:",
"LabelMetadataReadersHelp": "Rank your preferred local metadata sources in order of priority. The first file found will be read.",
"LabelMetadataDownloaders": "Metadata downloaders:",
"LabelMetadataDownloadersHelp": "Enable and rank your preferred metadata downloaders in order of priority. Lower priority downloaders will only be used to fill in missing information.",
"LabelMetadataSavers": "Metadata savers:",
"LabelMetadataSaversHelp": "Choose the file formats to save your metadata to.",
"LabelImageFetchers": "Image fetchers:",
"LabelImageFetchersHelp": "Enable and rank your preferred image fetchers in order of priority.",
"ButtonQueueAllFromHere": "Queue all from here",
"ButtonPlayAllFromHere": "Play all from here",
"LabelDynamicExternalId": "{0} Id:",
"HeaderIdentify": "Identify Item",
"PersonTypePerson": "Person",
"LabelTitleDisplayOrder": "Title display order:",
"OptionSortName": "Sort name",
"OptionReleaseDate": "Release date",
"LabelSeasonNumber": "Season number:",
"LabelDiscNumber": "Disc number",
"LabelParentNumber": "Parent number",
"LabelEpisodeNumber": "Episode number:",
"LabelTrackNumber": "Track number:",
"LabelNumber": "Number:",
"LabelReleaseDate": "Release date:",
"LabelEndDate": "End date:",
"LabelYear": "Year:",
"LabelDateOfBirth": "Date of birth:",
"LabelBirthYear": "Birth year:",
"LabelDeathDate": "Death date:",
"HeaderRemoveMediaLocation": "Remove Media Location",
"MessageConfirmRemoveMediaLocation": "Are you sure you wish to remove this location?",
"HeaderRenameMediaFolder": "Rename Media Folder",
"LabelNewName": "New name:",
"HeaderAddMediaFolder": "Add Media Folder",
"HeaderAddMediaFolderHelp": "Name (Movies, Music, TV, etc):",
"HeaderRemoveMediaFolder": "Remove Media Folder",
"MessageTheFollowingLocationWillBeRemovedFromLibrary": "The following media locations will be removed from your library:",
"MessageAreYouSureYouWishToRemoveMediaFolder": "Are you sure you wish to remove this media folder?",
"ButtonRename": "Rename",
"ButtonChangeType": "Change type",
"HeaderMediaLocations": "Media Locations",
"LabelFolderTypeValue": "Folder type: {0}",
"LabelPathSubstitutionHelp": "Optional: Path substitution can map server paths to network shares that clients can access for direct playback.",
"FolderTypeMixed": "Mixed movies & tv",
"FolderTypeMovies": "Movies",
"FolderTypeMusic": "Music",
"FolderTypeAdultVideos": "Adult videos",
"FolderTypePhotos": "Photos",
"FolderTypeMusicVideos": "Music videos",
"FolderTypeHomeVideos": "Home videos",
"FolderTypeGames": "Games",
"FolderTypeBooks": "Books",
"FolderTypeTvShows": "TV shows",
"TabMovies": "Movies",
"TabSeries": "Series",
"TabEpisodes": "Episodes",
"TabTrailers": "Trailers",
"TabGames": "Games",
"TabAlbums": "Albums",
"TabSongs": "Songs",
"TabMusicVideos": "Music Videos",
"BirthPlaceValue": "Birth place: {0}",
"DeathDateValue": "Died: {0}",
"BirthDateValue": "Born: {0}",
"HeaderLatestReviews": "Latest Reviews",
"HeaderPluginInstallation": "Plugin Installation",
"MessageAlreadyInstalled": "This version is already installed.",
"ValueReviewCount": "{0} Reviews",
"MessageYouHaveVersionInstalled": "You currently have version {0} installed.",
"MessageTrialExpired": "The trial period for this feature has expired",
"MessageTrialWillExpireIn": "The trial period for this feature will expire in {0} day(s)",
"MessageInstallPluginFromApp": "This plugin must be installed from with in the app you intend to use it in.",
"ValuePriceUSD": "Price: {0} (USD)",
"MessageFeatureIncludedWithSupporter": "You are registered for this feature, and will be able to continue using it with an active supporter membership.",
"MessageChangeRecurringPlanConfirm": "After completing this transaction you will need to cancel your previous recurring donation from within your PayPal account. Thank you for supporting Media Browser.",
"MessageSupporterMembershipExpiredOn": "Your supporter membership expired on {0}.",
"MessageYouHaveALifetimeMembership": "You have a lifetime supporter membership. You can provide additional donations on a one-time or recurring basis using the options below. Thank you for supporting Media Browser.",
"MessageYouHaveAnActiveRecurringMembership": "You have an active {0} membership. You can upgrade your plan using the options below.",
"ButtonDelete": "Delete",
"HeaderMediaBrowserAccountAdded": "Media Browser Account Added",
"MessageMediaBrowserAccountAdded": "The Media Browser account has been added to this user.",
"MessagePendingMediaBrowserAccountAdded": "The Media Browser account has been added to this user. An email will be sent to the owner of the account. The invitation will need to be confirmed by clicking a link within the email.",
"HeaderMediaBrowserAccountRemoved": "Media Browser Account Removed",
"MessageMediaBrowserAccontRemoved": "The Media Browser account has been removed from this user.",
"TooltipLinkedToMediaBrowserConnect": "Linked to Media Browser Connect",
"HeaderUnrated": "Unrated",
"ValueDiscNumber": "Disc {0}",
"HeaderUnknownDate": "Unknown Date",
"HeaderUnknownYear": "Unknown Year",
"ValueMinutes": "{0} min",
"ButtonPlayExternalPlayer": "Play with external player",
"HeaderSelectExternalPlayer": "Select External Player",
"HeaderExternalPlayerPlayback": "External Player Playback",
"ButtonImDone": "I'm Done",
"OptionWatched": "Watched",
"OptionUnwatched": "Unwatched",
"ExternalPlayerPlaystateOptionsHelp": "Specify how you would like to resume playing this video next time.",
"LabelMarkAs": "Mark as:",
"OptionInProgress": "In-Progress",
"LabelResumePoint": "Resume point:",
"ValueOneMovie": "1 movie",
"ValueMovieCount": "{0} movies",
"ValueOneTrailer": "1 trailer",
"ValueTrailerCount": "{0} trailers",
"ValueOneSeries": "1 series",
"ValueSeriesCount": "{0} series",
"ValueOneEpisode": "1 episode",
"ValueEpisodeCount": "{0} episodes",
"ValueOneGame": "1 game",
"ValueGameCount": "{0} games",
"ValueOneAlbum": "1 album",
"ValueAlbumCount": "{0} albums",
"ValueOneSong": "1 song",
"ValueSongCount": "{0} songs",
"ValueOneMusicVideo": "1 music video",
"ValueMusicVideoCount": "{0} music videos",
"HeaderOffline": "Offline",
"HeaderUnaired": "Unaired",
"HeaderMissing": "Missing",
"ButtonWebsite": "Website",
"TooltipFavorite": "Favorite",
"TooltipLike": "Like",
"TooltipDislike": "Dislike",
"TooltipPlayed": "Played",
"ValueSeriesYearToPresent": "{0}-Present",
"ValueAwards": "Awards: {0}",
"ValueBudget": "Budget: {0}",
"ValueRevenue": "Revenue: {0}",
"ValuePremiered": "Premiered {0}",
"ValuePremieres": "Premieres {0}",
"ValueStudio": "Studio: {0}",
"ValueStudios": "Studios: {0}",
"ValueSpecialEpisodeName": "Special - {0}",
"LabelLimit": "Limit:",
"ValueLinks": "Links: {0}",
"HeaderPeople": "People",
"HeaderCastAndCrew": "Cast & Crew",
"ValueArtist": "Artist: {0}",
"ValueArtists": "Artists: {0}",
"HeaderTags": "Tags",
"MediaInfoCameraMake": "Camera make",
"MediaInfoCameraModel": "Camera model",
"MediaInfoAltitude": "Altitude",
"MediaInfoAperture": "Aperture",
"MediaInfoExposureTime": "Exposure time",
"MediaInfoFocalLength": "Focal length",
"MediaInfoOrientation": "Orientation",
"MediaInfoIsoSpeedRating": "Iso speed rating",
"MediaInfoLatitude": "Latitude",
"MediaInfoLongitude": "Longitude",
"MediaInfoShutterSpeed": "Shutter speed",
"MediaInfoSoftware": "Software",
"HeaderIfYouLikeCheckTheseOut": "If you like {0}, check these out...",
"HeaderPlotKeywords": "Plot Keywords",
"HeaderMovies": "Movies",
"HeaderAlbums": "Albums",
"HeaderGames": "Games",
"HeaderBooks": "Books",
"HeaderEpisodes": "Episodes",
"HeaderSeasons": "Seasons",
"HeaderTracks": "Tracks",
"HeaderItems": "Items",
"HeaderOtherItems": "Other Items",
"ButtonFullReview": "Full review",
"ValueAsRole": "as {0}",
"ValueGuestStar": "Guest star",
"MediaInfoSize": "Size",
"MediaInfoPath": "Path",
"MediaInfoFormat": "Format",
"MediaInfoContainer": "Container",
"MediaInfoDefault": "Default",
"MediaInfoForced": "Forced",
"MediaInfoExternal": "External",
"MediaInfoTimestamp": "Timestamp",
"MediaInfoPixelFormat": "Pixel format",
"MediaInfoBitDepth": "Bit depth",
"MediaInfoSampleRate": "Sample rate",
"MediaInfoBitrate": "Bitrate",
"MediaInfoChannels": "Channels",
"MediaInfoLayout": "Layout",
"MediaInfoLanguage": "Language",
"MediaInfoCodec": "Codec",
"MediaInfoProfile": "Profile",
"MediaInfoLevel": "Level",
"MediaInfoAspectRatio": "Aspect ratio",
"MediaInfoResolution": "Resolution",
"MediaInfoAnamorphic": "Anamorphic",
"MediaInfoInterlaced": "Interlaced",
"MediaInfoFramerate": "Framerate",
"MediaInfoStreamTypeAudio": "Audio",
"MediaInfoStreamTypeData": "Data",
"MediaInfoStreamTypeVideo": "Video",
"MediaInfoStreamTypeSubtitle": "Subtitle",
"MediaInfoStreamTypeEmbeddedImage": "Embedded Image",
"MediaInfoRefFrames": "Ref frames",
"TabPlayback": "Playback",
"HeaderSelectCustomIntrosPath": "Select Custom Intros Path",
"HeaderRateAndReview": "Rate and Review",
"HeaderThankYou": "Thank You",
"MessageThankYouForYourReview": "Thank you for your review.",
"LabelYourRating": "Your rating:",
"LabelFullReview": "Full review:",
"LabelShortRatingDescription": "Short rating summary:",
"OptionIRecommendThisItem": "I recommend this item",
"WebClientTourContent": "View your recently added media, next episodes, and more. The green circles indicate how many unplayed items you have.",
"WebClientTourMovies": "Play movies, trailers and more from any device with a web browser",
"WebClientTourMouseOver": "Hold the mouse over any poster for quick access to important information",
"WebClientTourTapHold": "Tap and hold or right click any poster for a context menu",
"WebClientTourMetadataManager": "Click edit to open the metadata manager",
"WebClientTourPlaylists": "Easily create playlists and instant mixes, and play them on any device",
"WebClientTourCollections": "Create movie collections to group box sets together",
"WebClientTourUserPreferences1": "User preferences allow you to customize the way your library is presented in all of your Media Browser apps",
"WebClientTourUserPreferences2": "Configure your audio and subtitle language settings once, for every Media Browser app",
"WebClientTourUserPreferences3": "Design the web client home page to your liking",
"WebClientTourUserPreferences4": "Configure backdrops, theme songs and external players",
"WebClientTourMobile1": "The web client works great on smartphones and tablets...",
"WebClientTourMobile2": "and easily controls other devices and Media Browser apps",
"MessageEnjoyYourStay": "Enjoy your stay",
"DashboardTourDashboard": "The server dashboard allows you to monitor your server and your users. You'll always know who is doing what and where they are.",
"DashboardTourUsers": "Easily create user accounts for your friends and family, each with their own permissions, library access, parental controls and more.",
"DashboardTourCinemaMode": "Cinema mode brings the theater experience straight to your living room with the ability to play trailers and custom intros before the main feature.",
"DashboardTourChapters": "Enable chapter image generation for your videos for a more pleasing presentation while viewing.",
"DashboardTourSubtitles": "Automatically download subtitles for your videos in any language.",
"DashboardTourPlugins": "Install plugins such as internet video channels, live tv, metadata scanners, and more.",
"DashboardTourNotifications": "Automatically send notifications of server events to your mobile device, email and more.",
"DashboardTourScheduledTasks": "Easily manage long running operations with scheduled tasks. Decide when they run, and how often.",
"DashboardTourMobile": "The Media Browser dashboard works great on smartphones and tablets. Manage your server from the palm of your hand anytime, anywhere.",
"MessageRefreshQueued": "Refresh queued",
"TabDevices": "Devices",
"DeviceLastUsedByUserName": "Last used by {0}",
"HeaderDeleteDevice": "Delete Device",
"DeleteDeviceConfirmation": "Are you sure you wish to delete this device? It will reappear the next time a user signs in with it.",
"LabelEnableCameraUploadFor": "Enable camera upload for:",
"HeaderSelectUploadPath": "Select Upload Path",
"LabelEnableCameraUploadForHelp": "Uploads will occur automatically in the background when signed into Media Browser.",
"ErrorMessageStartHourGreaterThanEnd": "End time must be greater than the start time."
}

@ -362,6 +362,7 @@ namespace MediaBrowser.Server.Implementations.Localization
new LocalizatonOption{ Name="English (United Kingdom)", Value="en-GB"}, new LocalizatonOption{ Name="English (United Kingdom)", Value="en-GB"},
new LocalizatonOption{ Name="English (United States)", Value="en-us"}, new LocalizatonOption{ Name="English (United States)", Value="en-us"},
new LocalizatonOption{ Name="Catalan", Value="ca"}, new LocalizatonOption{ Name="Catalan", Value="ca"},
new LocalizatonOption{ Name="Chinese Simplified", Value="zh-CN"},
new LocalizatonOption{ Name="Chinese Traditional", Value="zh-TW"}, new LocalizatonOption{ Name="Chinese Traditional", Value="zh-TW"},
new LocalizatonOption{ Name="Croatian", Value="hr"}, new LocalizatonOption{ Name="Croatian", Value="hr"},
new LocalizatonOption{ Name="Czech", Value="cs"}, new LocalizatonOption{ Name="Czech", Value="cs"},

@ -152,7 +152,7 @@
"LabelWebsite": "Sito web:", "LabelWebsite": "Sito web:",
"ButtonAddLocalUser": "Aggiungi Utente locale", "ButtonAddLocalUser": "Aggiungi Utente locale",
"LabelTagline": "Messaggio pers:", "LabelTagline": "Messaggio pers:",
"ButtonInviteUser": "Invite User", "ButtonInviteUser": "Invita un utente",
"ButtonSave": "Salva", "ButtonSave": "Salva",
"LabelOverview": "Trama:", "LabelOverview": "Trama:",
"ButtonResetPassword": "Reset Password", "ButtonResetPassword": "Reset Password",
@ -404,11 +404,11 @@
"OptionFriday": "Venerdi", "OptionFriday": "Venerdi",
"LabelNumberTrailerToPlay": "Number of trailers to play:", "LabelNumberTrailerToPlay": "Number of trailers to play:",
"OptionSaturday": "Sabato", "OptionSaturday": "Sabato",
"TitleDevices": "Devices", "TitleDevices": "Dispositivi",
"HeaderManagement": "Gestione :", "HeaderManagement": "Gestione :",
"TabCameraUpload": "Camera Upload", "TabCameraUpload": "Camera Upload",
"LabelManagement": "Management:", "LabelManagement": "Management:",
"TabDevices": "Devices", "TabDevices": "Dispositivi",
"OptionMissingImdbId": "IMDB id mancante", "OptionMissingImdbId": "IMDB id mancante",
"HeaderCameraUploadHelp": "Automatically upload photos and videos taken from your mobile devices into Media Browser.", "HeaderCameraUploadHelp": "Automatically upload photos and videos taken from your mobile devices into Media Browser.",
"OptionMissingTvdbId": "TheTVDB Id mancante", "OptionMissingTvdbId": "TheTVDB Id mancante",
@ -436,7 +436,7 @@
"SearchKnowledgeBase": "Cerca sulla guida online", "SearchKnowledgeBase": "Cerca sulla guida online",
"HeaderGuests": "Guests", "HeaderGuests": "Guests",
"VisitTheCommunity": "Visita la nostra comunit\u00e0", "VisitTheCommunity": "Visita la nostra comunit\u00e0",
"HeaderLocalUsers": "Local Users", "HeaderLocalUsers": "Utenti locale",
"VisitMediaBrowserWebsite": "Visita il sito di Media Browser", "VisitMediaBrowserWebsite": "Visita il sito di Media Browser",
"HeaderPendingInvitations": "Pending Invitations", "HeaderPendingInvitations": "Pending Invitations",
"VisitMediaBrowserWebsiteLong": "Vuoi saperne di pi\u00f9 sulle ultime novit\u00e0?", "VisitMediaBrowserWebsiteLong": "Vuoi saperne di pi\u00f9 sulle ultime novit\u00e0?",
@ -689,12 +689,12 @@
"NewCollectionNameExample": "Esempio:Collezione Star wars", "NewCollectionNameExample": "Esempio:Collezione Star wars",
"OptionSearchForInternetMetadata": "Cerca su internet le immagini e i metadati", "OptionSearchForInternetMetadata": "Cerca su internet le immagini e i metadati",
"ButtonCreate": "Crea", "ButtonCreate": "Crea",
"LabelLocalHttpServerPortNumber": "Local port number:", "LabelLocalHttpServerPortNumber": "Numero di porta locale:",
"LabelLocalHttpServerPortNumberHelp": "The tcp port number that Media Browser's http server should bind to.", "LabelLocalHttpServerPortNumberHelp": "The tcp port number that Media Browser's http server should bind to.",
"LabelPublicPort": "Public port number:", "LabelPublicPort": "Public port number:",
"LabelPublicPortHelp": "The public port number that should be mapped to the local port.", "LabelPublicPortHelp": "The public port number that should be mapped to the local port.",
"LabelWebSocketPortNumber": "Porta webSocket numero:", "LabelWebSocketPortNumber": "Porta webSocket numero:",
"LabelEnableAutomaticPortMap": "Enable automatic port mapping", "LabelEnableAutomaticPortMap": "Abilita mappatura delle porte automatiche",
"LabelEnableAutomaticPortMapHelp": "Attempt to automatically map the public port to the local port via UPnP. This may not work with some router models.", "LabelEnableAutomaticPortMapHelp": "Attempt to automatically map the public port to the local port via UPnP. This may not work with some router models.",
"LabelExternalDDNS": "DDNS Esterno:", "LabelExternalDDNS": "DDNS Esterno:",
"LabelExternalDDNSHelp": "Se tu ha un DNS dinamico inseriscilo qui.Media browser lo utilizzera per le connessioni remote.", "LabelExternalDDNSHelp": "Se tu ha un DNS dinamico inseriscilo qui.Media browser lo utilizzera per le connessioni remote.",

@ -410,27 +410,27 @@
"LabelManagement": "\u041c\u0435\u0442\u0430\u0434\u0435\u0440\u0435\u043a\u0442\u0435\u0440", "LabelManagement": "\u041c\u0435\u0442\u0430\u0434\u0435\u0440\u0435\u043a\u0442\u0435\u0440",
"TabDevices": "\u0416\u0430\u0431\u0434\u044b\u049b\u0442\u0430\u0440", "TabDevices": "\u0416\u0430\u0431\u0434\u044b\u049b\u0442\u0430\u0440",
"OptionMissingImdbId": "IMDb Id \u0436\u043e\u049b", "OptionMissingImdbId": "IMDb Id \u0436\u043e\u049b",
"HeaderCameraUploadHelp": "Automatically upload photos and videos taken from your mobile devices into Media Browser.", "HeaderCameraUploadHelp": "\u04b0\u0442\u049b\u044b\u0440 \u0436\u0430\u0431\u0434\u044b\u049b\u0442\u0430\u0440\u044b\u04a3\u044b\u0437\u0431\u0435\u043d \u0442\u04af\u0441\u0456\u0440\u0456\u043b\u0433\u0435\u043d \u0444\u043e\u0442\u043e\u0441\u0443\u0440\u0435\u0442\u0442\u0435\u0440 \u043c\u0435\u043d \u0431\u0435\u0439\u043d\u0435\u0444\u0430\u0439\u043b\u0434\u0430\u0440\u0434\u044b Media Browser \u0456\u0448\u0456\u043d\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0442\u044b \u0442\u04af\u0440\u0434\u0435 \u049b\u043e\u0442\u0430\u0440\u044b\u043f \u0431\u0435\u0440\u0443.",
"OptionMissingTvdbId": "TheTVDB Id \u0436\u043e\u049b", "OptionMissingTvdbId": "TheTVDB Id \u0436\u043e\u049b",
"MessageNoDevicesSupportCameraUpload": "You currently don't have any devices that support camera upload.", "MessageNoDevicesSupportCameraUpload": "\u0410\u0493\u044b\u043c\u0434\u0430 \u043a\u0430\u043c\u0435\u0440\u0430\u0434\u0430\u043d \u049b\u043e\u0442\u0430\u0440\u044b\u043f \u0431\u0435\u0440\u0435\u0442\u0456\u043d \u0435\u0448\u049b\u0430\u043d\u0434\u0430\u0439 \u0436\u0430\u0431\u0434\u044b\u049b\u0442\u0430\u0440\u044b\u04a3\u044b\u0437 \u0436\u043e\u049b.",
"OptionMissingOverview": "\u0416\u0430\u043b\u043f\u044b \u0448\u043e\u043b\u0443 \u0436\u043e\u049b", "OptionMissingOverview": "\u0416\u0430\u043b\u043f\u044b \u0448\u043e\u043b\u0443 \u0436\u043e\u049b",
"LabelCameraUploadPath": "\u041a\u0430\u043c\u0435\u0440\u0430\u0434\u0430\u043d \u043a\u0435\u0440\u0456 \u049b\u043e\u0442\u0430\u0440\u0443 \u0436\u043e\u043b\u044b:", "LabelCameraUploadPath": "\u041a\u0430\u043c\u0435\u0440\u0430\u0434\u0430\u043d \u043a\u0435\u0440\u0456 \u049b\u043e\u0442\u0430\u0440\u0443 \u0436\u043e\u043b\u044b:",
"OptionFileMetadataYearMismatch": "\u0424\u0430\u0439\u043b\/\u043c\u0435\u0442\u0430\u0434\u0435\u0440\u0435\u043a \u0436\u044b\u043b\u044b \u0441\u04d9\u0439\u043a\u0435\u0441 \u0435\u043c\u0435\u0441", "OptionFileMetadataYearMismatch": "\u0424\u0430\u0439\u043b\/\u043c\u0435\u0442\u0430\u0434\u0435\u0440\u0435\u043a \u0436\u044b\u043b\u044b \u0441\u04d9\u0439\u043a\u0435\u0441 \u0435\u043c\u0435\u0441",
"LabelCameraUploadPathHelp": "Select a custom upload path, if desired. If unspecified a default folder will be used.", "LabelCameraUploadPathHelp": "\u0415\u0433\u0435\u0440 \u043a\u0435\u0440\u0435\u043a \u0431\u043e\u043b\u0441\u0430, \u049b\u043e\u0442\u0430\u0440\u044b\u043f \u0431\u0435\u0440\u0443 \u04af\u0448\u0456\u043d \u0442\u0435\u04a3\u0448\u0435\u043b\u0433\u0435\u043d \u0436\u043e\u043b\u0434\u044b \u0442\u0430\u04a3\u0434\u0430\u04a3\u044b\u0437. \u0415\u0433\u0435\u0440 \u043a\u04e9\u0440\u0441\u0435\u0442\u0456\u043b\u043c\u0435\u0441\u0435, \u04d9\u0434\u0435\u043f\u043a\u0456 \u049b\u0430\u043b\u0442\u0430 \u043f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u044b\u043b\u0430\u0434\u044b.",
"TabGeneral": "\u0416\u0430\u043b\u043f\u044b", "TabGeneral": "\u0416\u0430\u043b\u043f\u044b",
"LabelCreateCameraUploadSubfolder": "\u04d8\u0440\u049b\u0430\u0439\u0441\u044b \u0436\u0430\u0431\u0434\u044b\u049b \u04af\u0448\u0456\u043d \u0456\u0448\u043a\u0456 \u049b\u0430\u043b\u0442\u0430 \u0436\u0430\u0441\u0430\u0443", "LabelCreateCameraUploadSubfolder": "\u04d8\u0440\u049b\u0430\u0439\u0441\u044b \u0436\u0430\u0431\u0434\u044b\u049b \u04af\u0448\u0456\u043d \u0456\u0448\u043a\u0456 \u049b\u0430\u043b\u0442\u0430 \u0436\u0430\u0441\u0430\u0443",
"TitleSupport": "\u049a\u043e\u043b\u0434\u0430\u0443", "TitleSupport": "\u049a\u043e\u043b\u0434\u0430\u0443",
"LabelCreateCameraUploadSubfolderHelp": "Specific folders can be assigned to a device by clicking on it from the Devices page.", "LabelCreateCameraUploadSubfolderHelp": "\u0416\u0430\u0431\u0434\u044b\u049b\u0442\u0430\u0440 \u0431\u0435\u0442\u0456\u043d\u0434\u0435 \u043d\u04b1\u049b\u044b\u0493\u0430\u043d\u0434\u0430 \u0436\u0430\u0431\u0434\u044b\u049b\u049b\u0430 \u043d\u0430\u049b\u0442\u044b \u049b\u0430\u043b\u0442\u0430\u043b\u0430\u0440 \u0442\u0430\u0493\u0430\u0439\u044b\u043d\u0434\u0430\u043b\u0443\u044b \u043c\u04af\u043c\u043a\u0456\u043d.",
"TabLog": "\u0416\u04b1\u0440\u043d\u0430\u043b", "TabLog": "\u0416\u04b1\u0440\u043d\u0430\u043b",
"LabelCustomDeviceDisplayName": "\u0411\u0435\u0439\u043d\u0435\u043b\u0435\u043d\u0443 \u0430\u0442\u044b:", "LabelCustomDeviceDisplayName": "\u0411\u0435\u0439\u043d\u0435\u043b\u0435\u043d\u0443 \u0430\u0442\u044b:",
"TabAbout": "\u0422\u0443\u0440\u0430\u043b\u044b", "TabAbout": "\u0422\u0443\u0440\u0430\u043b\u044b",
"LabelCustomDeviceDisplayNameHelp": "Supply a custom display name or leave empty to use the name reported by the device.", "LabelCustomDeviceDisplayNameHelp": "\u0411\u0435\u0439\u043d\u0435\u043b\u0435\u043d\u0435\u0442\u0456\u043d \u0442\u0435\u04a3\u0448\u0435\u043b\u0433\u0435\u043d \u0430\u0442\u044b\u043d \u04b1\u0441\u044b\u043d\u044b\u04a3\u044b\u0437 \u043d\u0435\u043c\u0435\u0441\u0435 \u0436\u0430\u0431\u0434\u044b\u049b \u0430\u0440\u049b\u044b\u043b\u044b \u0431\u0430\u044f\u043d\u0434\u0430\u043b\u0493\u0430\u043d \u0430\u0442\u044b\u043d \u043f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443 \u04af\u0448\u0456\u043d \u0431\u043e\u0441 \u049b\u0430\u043b\u0434\u044b\u0440\u044b\u04a3\u044b\u0437.",
"TabSupporterKey": "\u049a\u043e\u043b\u0434\u0430\u0443\u0448\u044b \u043a\u0456\u043b\u0442\u0456", "TabSupporterKey": "\u049a\u043e\u043b\u0434\u0430\u0443\u0448\u044b \u043a\u0456\u043b\u0442\u0456",
"HeaderInviteUser": "\u041f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b\u043d\u044b \u0448\u0430\u049b\u044b\u0440\u0443", "HeaderInviteUser": "\u041f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b\u043d\u044b \u0448\u0430\u049b\u044b\u0440\u0443",
"TabBecomeSupporter": "\u049a\u043e\u043b\u0434\u0430\u0443\u0448\u044b \u0431\u043e\u043b\u0443", "TabBecomeSupporter": "\u049a\u043e\u043b\u0434\u0430\u0443\u0448\u044b \u0431\u043e\u043b\u0443",
"LabelConnectInviteUserHelp": "\u0411\u04b1\u043b \u0434\u043e\u0441\u0442\u0430\u0440\u044b\u04a3\u044b\u0437\u0434\u044b\u04a3 Media Browser \u0493\u0430\u043b\u0430\u043c\u0442\u043e\u0440 \u0441\u0430\u0439\u0442\u044b\u043d\u0430 \u043a\u0456\u0440\u0433\u0435\u043d\u0434\u0435 \u049b\u043e\u043b\u0434\u0430\u043d\u0430\u0442\u044b\u043d \u043f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b \u0430\u0442\u044b \u043d\u0435\u043c\u0435\u0441\u0435 \u0435-\u043f\u043e\u0448\u0442\u0430 \u043c\u0435\u043a\u0435\u043d\u0436\u0430\u0439\u044b \u0431\u043e\u043b\u044b\u043f \u0442\u0430\u0431\u044b\u043b\u0430\u0434\u044b.", "LabelConnectInviteUserHelp": "\u0411\u04b1\u043b \u0434\u043e\u0441\u0442\u0430\u0440\u044b\u04a3\u044b\u0437\u0434\u044b\u04a3 Media Browser \u0493\u0430\u043b\u0430\u043c\u0442\u043e\u0440 \u0441\u0430\u0439\u0442\u044b\u043d\u0430 \u043a\u0456\u0440\u0433\u0435\u043d\u0434\u0435 \u049b\u043e\u043b\u0434\u0430\u043d\u0430\u0442\u044b\u043d \u043f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b \u0430\u0442\u044b \u043d\u0435\u043c\u0435\u0441\u0435 \u0435-\u043f\u043e\u0448\u0442\u0430 \u043c\u0435\u043a\u0435\u043d\u0436\u0430\u0439\u044b \u0431\u043e\u043b\u044b\u043f \u0442\u0430\u0431\u044b\u043b\u0430\u0434\u044b.",
"MediaBrowserHasCommunity": "Media Browser \u043f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b\u043b\u0430\u0440\u044b \u0431\u0435\u043d \u049b\u043e\u043b\u0434\u0430\u0443\u0448\u044b\u043b\u0430\u0440\u0434\u044b\u04a3 \u0434\u0430\u043c\u0443\u0434\u0430\u0493\u044b \u049b\u0430\u0443\u044b\u043c\u0434\u0430\u0441\u0442\u044b\u0493\u044b \u0431\u0430\u0440.", "MediaBrowserHasCommunity": "Media Browser \u043f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b\u043b\u0430\u0440\u044b \u0431\u0435\u043d \u049b\u043e\u043b\u0434\u0430\u0443\u0448\u044b\u043b\u0430\u0440\u0434\u044b\u04a3 \u0434\u0430\u043c\u0443\u0434\u0430\u0493\u044b \u049b\u0430\u0443\u044b\u043c\u0434\u0430\u0441\u0442\u044b\u0493\u044b \u0431\u0430\u0440.",
"HeaderInviteUserHelp": "Sharing your media with friends is easier than ever before with Media Browser Connect.", "HeaderInviteUserHelp": "Media Browser Connect \u0430\u0440\u049b\u044b\u043b\u044b \u0442\u0430\u0441\u0443\u0448\u044b\u0434\u0435\u0440\u0435\u043a\u0442\u0435\u0440\u0434\u0456 \u0434\u043e\u0441\u0442\u0430\u0440\u044b\u04a3\u044b\u0437\u0431\u0435\u043d \u043e\u0440\u0442\u0430\u049b\u0442\u0430\u0441\u0443 \u0431\u04b1\u0440\u044b\u043d\u043d\u0430\u043d \u0434\u0430 \u0436\u0435\u04a3\u0456\u043b\u0434\u0435\u0443 \u0431\u043e\u043b\u0434\u044b.",
"CheckoutKnowledgeBase": "Media Browser \u0435\u04a3 \u04af\u043b\u043a\u0435\u043d \u049b\u0430\u0439\u0442\u0430\u0440\u044b\u043c\u0434\u044b\u043b\u044b\u0493\u044b\u043d \u0430\u043b\u0443 \u0436\u04e9\u043d\u0456\u043d\u0434\u0435 \u043a\u04e9\u043c\u0435\u043a\u0442\u0435\u0441\u0443 \u04af\u0448\u0456\u043d \u0411\u0456\u043b\u0456\u043c \u049b\u043e\u0440\u044b\u043d \u049b\u0430\u0440\u0430\u043f \u0448\u044b\u0493\u044b\u04a3\u044b\u0437.", "CheckoutKnowledgeBase": "Media Browser \u0435\u04a3 \u04af\u043b\u043a\u0435\u043d \u049b\u0430\u0439\u0442\u0430\u0440\u044b\u043c\u0434\u044b\u043b\u044b\u0493\u044b\u043d \u0430\u043b\u0443 \u0436\u04e9\u043d\u0456\u043d\u0434\u0435 \u043a\u04e9\u043c\u0435\u043a\u0442\u0435\u0441\u0443 \u04af\u0448\u0456\u043d \u0411\u0456\u043b\u0456\u043c \u049b\u043e\u0440\u044b\u043d \u049b\u0430\u0440\u0430\u043f \u0448\u044b\u0493\u044b\u04a3\u044b\u0437.",
"ButtonSendInvitation": "\u0428\u0430\u049b\u044b\u0440\u044b\u043c\u0434\u044b \u0436\u0456\u0431\u0435\u0440\u0443", "ButtonSendInvitation": "\u0428\u0430\u049b\u044b\u0440\u044b\u043c\u0434\u044b \u0436\u0456\u0431\u0435\u0440\u0443",
"SearchKnowledgeBase": "\u0411\u0456\u043b\u0456\u043c \u049b\u043e\u0440\u044b\u043d\u0430\u043d \u0456\u0437\u0434\u0435\u0443", "SearchKnowledgeBase": "\u0411\u0456\u043b\u0456\u043c \u049b\u043e\u0440\u044b\u043d\u0430\u043d \u0456\u0437\u0434\u0435\u0443",

@ -440,19 +440,19 @@
"VisitMediaBrowserWebsite": "Visitar o Web Site do Media Browser", "VisitMediaBrowserWebsite": "Visitar o Web Site do Media Browser",
"HeaderPendingInvitations": "Convites pendentes", "HeaderPendingInvitations": "Convites pendentes",
"VisitMediaBrowserWebsiteLong": "Visite o Web Site do Media Browser para obter as \u00faltimas novidades e atualizar-se com o blog de desenvolvedores.", "VisitMediaBrowserWebsiteLong": "Visite o Web Site do Media Browser para obter as \u00faltimas novidades e atualizar-se com o blog de desenvolvedores.",
"TabParentalControl": "Parental Control", "TabParentalControl": "Controle Parental",
"OptionHideUser": "Ocultar este usu\u00e1rio das telas de login", "OptionHideUser": "Ocultar este usu\u00e1rio das telas de login",
"HeaderAccessSchedule": "Access Schedule", "HeaderAccessSchedule": "Agendamento de Acesso",
"OptionDisableUser": "Desativar este usu\u00e1rio", "OptionDisableUser": "Desativar este usu\u00e1rio",
"HeaderAccessScheduleHelp": "Create an access schedule to limit access to certain hours.", "HeaderAccessScheduleHelp": "Criar um agendamento de acesso para limitar o acesso a certas horas.",
"OptionDisableUserHelp": "Se estiver desativado o servidor n\u00e3o permitir\u00e1 nenhuma conex\u00e3o deste usu\u00e1rio. Conex\u00f5es existentes ser\u00e3o abruptamente terminadas.", "OptionDisableUserHelp": "Se estiver desativado o servidor n\u00e3o permitir\u00e1 nenhuma conex\u00e3o deste usu\u00e1rio. Conex\u00f5es existentes ser\u00e3o abruptamente terminadas.",
"ButtonAddSchedule": "Add Schedule", "ButtonAddSchedule": "Adicionar Agendamento",
"HeaderAdvancedControl": "Controle Avan\u00e7ado", "HeaderAdvancedControl": "Controle Avan\u00e7ado",
"LabelAccessDay": "Day of week:", "LabelAccessDay": "Dia da semana:",
"LabelName": "Nome:", "LabelName": "Nome:",
"LabelAccessStart": "Start hour:", "LabelAccessStart": "Hora inicial:",
"OptionAllowUserToManageServer": "Permitir a este usu\u00e1rio administrar o servidor", "OptionAllowUserToManageServer": "Permitir a este usu\u00e1rio administrar o servidor",
"LabelAccessEnd": "End hour:", "LabelAccessEnd": "Hora final:",
"HeaderFeatureAccess": "Acesso aos Recursos", "HeaderFeatureAccess": "Acesso aos Recursos",
"OptionAllowMediaPlayback": "Permitir reprodu\u00e7\u00e3o de m\u00eddia", "OptionAllowMediaPlayback": "Permitir reprodu\u00e7\u00e3o de m\u00eddia",
"OptionAllowBrowsingLiveTv": "Permitir navega\u00e7\u00e3o na tv ao vivo", "OptionAllowBrowsingLiveTv": "Permitir navega\u00e7\u00e3o na tv ao vivo",

@ -1167,7 +1167,7 @@
"LabelExtractChaptersDuringLibraryScan": "Extract chapter images during the library scan", "LabelExtractChaptersDuringLibraryScan": "Extract chapter images during the library scan",
"LabelExtractChaptersDuringLibraryScanHelp": "If enabled, chapter images will be extracted when videos are imported during the library scan. If disabled they will be extracted during the chapter images scheduled task, allowing the regular library scan to complete faster.", "LabelExtractChaptersDuringLibraryScanHelp": "If enabled, chapter images will be extracted when videos are imported during the library scan. If disabled they will be extracted during the chapter images scheduled task, allowing the regular library scan to complete faster.",
"LabelConnectUserName": "Media Browser username/email:", "LabelConnectUserName": "Media Browser username/email:",
"LabelConnectUserNameHelp": "Connect this user to a Media Browser account to enable easy sign-in access from any app without having to know the server ip address.", "LabelConnectUserNameHelp": "Connect this user to a Media Browser account to enable easy sign-in access from Media Browser any app without having to know the server ip address.",
"ButtonLearnMoreAboutMediaBrowserConnect": "Learn more about Media Browser Connect", "ButtonLearnMoreAboutMediaBrowserConnect": "Learn more about Media Browser Connect",
"LabelExternalPlayers": "External players:", "LabelExternalPlayers": "External players:",
"LabelExternalPlayersHelp": "Display buttons to play content in external players. This is only available on devices that support url schemes, generally Android and iOS. With external players there is generally no support for remote control or resuming.", "LabelExternalPlayersHelp": "Display buttons to play content in external players. This is only available on devices that support url schemes, generally Android and iOS. With external players there is generally no support for remote control or resuming.",
@ -1246,5 +1246,6 @@
"OptionEveryday": "Every day", "OptionEveryday": "Every day",
"OptionWeekdays": "Weekdays", "OptionWeekdays": "Weekdays",
"OptionWeekends": "Weekends", "OptionWeekends": "Weekends",
"MessageProfileInfoSynced": "User profile information synced with Media Browser Connect." "MessageProfileInfoSynced": "User profile information synced with Media Browser Connect.",
"HeaderOptionalLinkMediaBrowserAccount": "Optional: Link your Media Browser account"
} }

@ -240,7 +240,8 @@
<Compile Include="Persistence\SqliteShrinkMemoryTimer.cs" /> <Compile Include="Persistence\SqliteShrinkMemoryTimer.cs" />
<Compile Include="Persistence\TypeMapper.cs" /> <Compile Include="Persistence\TypeMapper.cs" />
<Compile Include="Playlists\ManualPlaylistsFolder.cs" /> <Compile Include="Playlists\ManualPlaylistsFolder.cs" />
<Compile Include="Playlists\PlaylistImageEnhancer.cs" /> <Compile Include="Photos\PhotoAlbumImageProvider.cs" />
<Compile Include="Playlists\PlaylistImageProvider.cs" />
<Compile Include="Playlists\PlaylistManager.cs" /> <Compile Include="Playlists\PlaylistManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ScheduledTasks\PeopleValidationTask.cs" /> <Compile Include="ScheduledTasks\PeopleValidationTask.cs" />
@ -400,6 +401,8 @@
<EmbeddedResource Include="Localization\Server\tr.json" /> <EmbeddedResource Include="Localization\Server\tr.json" />
<EmbeddedResource Include="Localization\JavaScript\hr.json" /> <EmbeddedResource Include="Localization\JavaScript\hr.json" />
<EmbeddedResource Include="Localization\Server\hr.json" /> <EmbeddedResource Include="Localization\Server\hr.json" />
<EmbeddedResource Include="Localization\JavaScript\zh_CN.json" />
<EmbeddedResource Include="Localization\Server\zh_CN.json" />
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -511,13 +514,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- 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.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

@ -1,44 +1,115 @@
using MediaBrowser.Common.IO; using System;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
using MoreLinq;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MoreLinq;
namespace MediaBrowser.Server.Implementations.Playlists namespace MediaBrowser.Server.Implementations.Photos
{ {
public class PlaylistImageEnhancer : IImageEnhancer public class PhotoAlbumImageProvider : ICustomMetadataProvider<PhotoAlbum>, IHasChangeMonitor
{ {
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IProviderManager _provider;
public PlaylistImageEnhancer(IFileSystem fileSystem) public PhotoAlbumImageProvider(IFileSystem fileSystem, IProviderManager provider)
{ {
_fileSystem = fileSystem; _fileSystem = fileSystem;
_provider = provider;
} }
public bool Supports(IHasImages item, ImageType imageType) public async Task<ItemUpdateType> FetchAsync(PhotoAlbum item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{ {
return (imageType == ImageType.Primary || imageType == ImageType.Thumb) && item is Playlist; var primaryResult = await FetchAsync(item, ImageType.Primary, options, cancellationToken).ConfigureAwait(false);
var thumbResult = await FetchAsync(item, ImageType.Thumb, options, cancellationToken).ConfigureAwait(false);
return primaryResult | thumbResult;
} }
public MetadataProviderPriority Priority private Task<ItemUpdateType> FetchAsync(IHasImages item, ImageType imageType, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
var items = GetItemsWithImages(item);
var cacheKey = GetConfigurationCacheKey(items);
if (!HasChanged(item, imageType, cacheKey))
{
return Task.FromResult(ItemUpdateType.None);
}
return FetchAsyncInternal(item, imageType, cacheKey, options, cancellationToken);
}
private async Task<ItemUpdateType> FetchAsyncInternal(IHasImages item, ImageType imageType, string cacheKey, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
var img = await CreateImageAsync(item, imageType, 0).ConfigureAwait(false);
if (img == null)
{
return ItemUpdateType.None;
}
using (var ms = new MemoryStream())
{
img.Save(ms, ImageFormat.Png);
ms.Position = 0;
await _provider.SaveImage(item, ms, "image/png", imageType, null, cacheKey, cancellationToken).ConfigureAwait(false);
}
return ItemUpdateType.ImageUpdate;
}
private bool HasChanged(IHasImages item, ImageType type, string cacheKey)
{
var image = item.GetImageInfo(type, 0);
if (image != null)
{
if (!_fileSystem.ContainsSubPath(item.GetInternalMetadataPath(), image.Path))
{
return false;
}
var currentPathCacheKey = (Path.GetFileNameWithoutExtension(image.Path) ?? string.Empty).Split('_').LastOrDefault();
if (string.Equals(cacheKey, currentPathCacheKey, StringComparison.OrdinalIgnoreCase))
{
return false;
}
}
return true;
}
private const string Version = "3";
public string GetConfigurationCacheKey(List<BaseItem> items)
{ {
get { return MetadataProviderPriority.First; } return (Version + "_" + string.Join(",", items.Select(i => i.Id.ToString("N")).ToArray())).GetMD5().ToString("N");
} }
private List<BaseItem> GetItemsWithImages(IHasImages item) private List<BaseItem> GetItemsWithImages(IHasImages item)
{ {
var photoAlbum = item as PhotoAlbum;
if (photoAlbum != null)
{
return GetFinalItems(photoAlbum.RecursiveChildren.Where(i => i is Photo).ToList());
}
var playlist = (Playlist)item; var playlist = (Playlist)item;
var items = playlist.GetManageableItems() var items = playlist.GetManageableItems()
@ -78,6 +149,11 @@ namespace MediaBrowser.Server.Implementations.Playlists
.DistinctBy(i => i.Id) .DistinctBy(i => i.Id)
.ToList(); .ToList();
return GetFinalItems(items);
}
private List<BaseItem> GetFinalItems(List<BaseItem> items)
{
// Rotate the images no more than once per day // Rotate the images no more than once per day
var random = new Random(DateTime.Now.DayOfYear).Next(); var random = new Random(DateTime.Now.DayOfYear).Next();
@ -88,66 +164,18 @@ namespace MediaBrowser.Server.Implementations.Playlists
.ToList(); .ToList();
} }
private const string Version = "3"; public async Task<Image> CreateImageAsync(IHasImages item, ImageType imageType, int imageIndex)
public string GetConfigurationCacheKey(List<BaseItem> items)
{
return Version + "_" + string.Join(",", items.Select(i => i.Id.ToString("N")).ToArray());
}
public string GetConfigurationCacheKey(IHasImages item, ImageType imageType)
{
var items = GetItemsWithImages(item);
return GetConfigurationCacheKey(items);
}
private const int SquareImageSize = 800;
private const int ThumbImageWidth = 1600;
private const int ThumbImageHeight = 900;
public ImageSize GetEnhancedImageSize(IHasImages item, ImageType imageType, int imageIndex, ImageSize originalImageSize)
{
var items = GetItemsWithImages(item);
if (items.Count == 0)
{
return originalImageSize;
}
if (imageType == ImageType.Thumb)
{
return new ImageSize
{
Height = ThumbImageHeight,
Width = ThumbImageWidth
};
}
return new ImageSize
{
Height = SquareImageSize,
Width = SquareImageSize
};
}
public async Task<Image> EnhanceImageAsync(IHasImages item, Image originalImage, ImageType imageType, int imageIndex)
{ {
var items = GetItemsWithImages(item); var items = GetItemsWithImages(item);
if (items.Count == 0) if (items.Count == 0)
{ {
return originalImage; return null;
} }
var img = imageType == ImageType.Thumb ? return imageType == ImageType.Thumb ?
await GetThumbCollage(items).ConfigureAwait(false) : await GetThumbCollage(items).ConfigureAwait(false) :
await GetSquareCollage(items).ConfigureAwait(false); await GetSquareCollage(items).ConfigureAwait(false);
using (originalImage)
{
return img;
}
} }
private Task<Image> GetThumbCollage(List<BaseItem> items) private Task<Image> GetThumbCollage(List<BaseItem> items)
@ -217,6 +245,10 @@ namespace MediaBrowser.Server.Implementations.Playlists
return img; return img;
} }
private const int SquareImageSize = 800;
private const int ThumbImageWidth = 1600;
private const int ThumbImageHeight = 900;
private async Task<Image> GetSquareCollage(List<string> files) private async Task<Image> GetSquareCollage(List<string> files)
{ {
if (files.Count < 4) if (files.Count < 4)
@ -288,5 +320,18 @@ namespace MediaBrowser.Server.Implementations.Playlists
return Image.FromStream(memoryStream, true, false); return Image.FromStream(memoryStream, true, false);
} }
} }
public string Name
{
get { return "Dynamic Image Provider"; }
}
public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date)
{
var items = GetItemsWithImages(item);
var cacheKey = GetConfigurationCacheKey(items);
return HasChanged(item, ImageType.Primary, cacheKey) || HasChanged(item, ImageType.Thumb, cacheKey);
}
} }
} }

@ -0,0 +1,345 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MoreLinq;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Playlists
{
public class PlaylistImageProvider : ICustomMetadataProvider<Playlist>, IHasChangeMonitor
{
private readonly IFileSystem _fileSystem;
private readonly IProviderManager _provider;
public PlaylistImageProvider(IFileSystem fileSystem, IProviderManager provider)
{
_fileSystem = fileSystem;
_provider = provider;
}
public async Task<ItemUpdateType> FetchAsync(Playlist item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
var primaryResult = await FetchAsync(item, ImageType.Primary, options, cancellationToken).ConfigureAwait(false);
var thumbResult = await FetchAsync(item, ImageType.Thumb, options, cancellationToken).ConfigureAwait(false);
return primaryResult | thumbResult;
}
public async Task<ItemUpdateType> FetchAsync(PhotoAlbum item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
var primaryResult = await FetchAsync(item, ImageType.Primary, options, cancellationToken).ConfigureAwait(false);
var thumbResult = await FetchAsync(item, ImageType.Thumb, options, cancellationToken).ConfigureAwait(false);
return primaryResult | thumbResult;
}
private Task<ItemUpdateType> FetchAsync(IHasImages item, ImageType imageType, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
var items = GetItemsWithImages(item);
var cacheKey = GetConfigurationCacheKey(items);
if (!HasChanged(item, imageType, cacheKey))
{
return Task.FromResult(ItemUpdateType.None);
}
return FetchAsyncInternal(item, imageType, cacheKey, options, cancellationToken);
}
private async Task<ItemUpdateType> FetchAsyncInternal(IHasImages item, ImageType imageType, string cacheKey, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
var img = await CreateImageAsync(item, imageType, 0).ConfigureAwait(false);
if (img == null)
{
return ItemUpdateType.None;
}
using (var ms = new MemoryStream())
{
img.Save(ms, ImageFormat.Png);
ms.Position = 0;
await _provider.SaveImage(item, ms, "image/png", imageType, null, cacheKey, cancellationToken).ConfigureAwait(false);
}
return ItemUpdateType.ImageUpdate;
}
private bool HasChanged(IHasImages item, ImageType type, string cacheKey)
{
var image = item.GetImageInfo(type, 0);
if (image != null)
{
if (!_fileSystem.ContainsSubPath(item.GetInternalMetadataPath(), image.Path))
{
return false;
}
var currentPathCacheKey = (Path.GetFileNameWithoutExtension(image.Path) ?? string.Empty).Split('_').LastOrDefault();
if (string.Equals(cacheKey, currentPathCacheKey, StringComparison.OrdinalIgnoreCase))
{
return false;
}
}
return true;
}
private const string Version = "3";
public string GetConfigurationCacheKey(List<BaseItem> items)
{
return (Version + "_" + string.Join(",", items.Select(i => i.Id.ToString("N")).ToArray())).GetMD5().ToString("N");
}
private List<BaseItem> GetItemsWithImages(IHasImages item)
{
var photoAlbum = item as PhotoAlbum;
if (photoAlbum != null)
{
return GetFinalItems(photoAlbum.RecursiveChildren.Where(i => i is Photo).ToList());
}
var playlist = (Playlist)item;
var items = playlist.GetManageableItems()
.Select(i =>
{
var subItem = i.Item2;
var episode = subItem as Episode;
if (episode != null)
{
var series = episode.Series;
if (series != null && series.HasImage(ImageType.Primary))
{
return series;
}
}
if (subItem.HasImage(ImageType.Primary))
{
return subItem;
}
var parent = subItem.Parent;
if (parent != null && parent.HasImage(ImageType.Primary))
{
if (parent is MusicAlbum)
{
return parent;
}
}
return null;
})
.Where(i => i != null)
.DistinctBy(i => i.Id)
.ToList();
return GetFinalItems(items);
}
private List<BaseItem> GetFinalItems(List<BaseItem> items)
{
// Rotate the images no more than once per day
var random = new Random(DateTime.Now.DayOfYear).Next();
return items
.OrderBy(i => random - items.IndexOf(i))
.Take(4)
.OrderBy(i => i.Name)
.ToList();
}
public async Task<Image> CreateImageAsync(IHasImages item, ImageType imageType, int imageIndex)
{
var items = GetItemsWithImages(item);
if (items.Count == 0)
{
return null;
}
return imageType == ImageType.Thumb ?
await GetThumbCollage(items).ConfigureAwait(false) :
await GetSquareCollage(items).ConfigureAwait(false);
}
private Task<Image> GetThumbCollage(List<BaseItem> items)
{
return GetThumbCollage(items.Select(i => i.GetImagePath(ImageType.Primary)).ToList());
}
private Task<Image> GetSquareCollage(List<BaseItem> items)
{
return GetSquareCollage(items.Select(i => i.GetImagePath(ImageType.Primary)).ToList());
}
private async Task<Image> GetThumbCollage(List<string> files)
{
if (files.Count < 3)
{
return await GetSingleImage(files).ConfigureAwait(false);
}
const int rows = 1;
const int cols = 3;
const int cellWidth = 2 * (ThumbImageWidth / 3);
const int cellHeight = ThumbImageHeight;
var index = 0;
var img = new Bitmap(ThumbImageWidth, ThumbImageHeight, PixelFormat.Format32bppPArgb);
using (var graphics = Graphics.FromImage(img))
{
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingMode = CompositingMode.SourceCopy;
for (var row = 0; row < rows; row++)
{
for (var col = 0; col < cols; col++)
{
var x = col * (cellWidth / 2);
var y = row * cellHeight;
if (files.Count > index)
{
using (var fileStream = _fileSystem.GetFileStream(files[index], FileMode.Open, FileAccess.Read, FileShare.Read, true))
{
using (var memoryStream = new MemoryStream())
{
await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0;
using (var imgtemp = Image.FromStream(memoryStream, true, false))
{
graphics.DrawImage(imgtemp, x, y, cellWidth, cellHeight);
}
}
}
}
index++;
}
}
}
return img;
}
private const int SquareImageSize = 800;
private const int ThumbImageWidth = 1600;
private const int ThumbImageHeight = 900;
private async Task<Image> GetSquareCollage(List<string> files)
{
if (files.Count < 4)
{
return await GetSingleImage(files).ConfigureAwait(false);
}
const int rows = 2;
const int cols = 2;
const int singleSize = SquareImageSize / 2;
var index = 0;
var img = new Bitmap(SquareImageSize, SquareImageSize, PixelFormat.Format32bppPArgb);
using (var graphics = Graphics.FromImage(img))
{
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingMode = CompositingMode.SourceCopy;
for (var row = 0; row < rows; row++)
{
for (var col = 0; col < cols; col++)
{
var x = col * singleSize;
var y = row * singleSize;
using (var fileStream = _fileSystem.GetFileStream(files[index], FileMode.Open, FileAccess.Read, FileShare.Read, true))
{
using (var memoryStream = new MemoryStream())
{
await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0;
using (var imgtemp = Image.FromStream(memoryStream, true, false))
{
graphics.DrawImage(imgtemp, x, y, singleSize, singleSize);
}
}
}
index++;
}
}
}
return img;
}
private Task<Image> GetSingleImage(List<string> files)
{
return GetImage(files[0]);
}
private async Task<Image> GetImage(string file)
{
using (var fileStream = _fileSystem.GetFileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, true))
{
var memoryStream = new MemoryStream();
await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0;
return Image.FromStream(memoryStream, true, false);
}
}
public string Name
{
get { return "Dynamic Image Provider"; }
}
public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date)
{
var items = GetItemsWithImages(item);
var cacheKey = GetConfigurationCacheKey(items);
return HasChanged(item, ImageType.Primary, cacheKey) || HasChanged(item, ImageType.Thumb, cacheKey);
}
}
}

@ -189,11 +189,4 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
</Project> </Project>

@ -266,11 +266,4 @@ del "$(SolutionDir)..\Deploy\MBServer.zip"
</GetAssemblyIdentity> </GetAssemblyIdentity>
<Exec Command="copy &quot;$(SolutionDir)..\Deploy\MBServer.zip&quot; &quot;$(SolutionDir)..\Deploy\MBServer_%(CurrentAssembly.Version).zip&quot; /y" Condition="'$(ConfigurationName)' == 'Release'" /> <Exec Command="copy &quot;$(SolutionDir)..\Deploy\MBServer.zip&quot; &quot;$(SolutionDir)..\Deploy\MBServer_%(CurrentAssembly.Version).zip&quot; /y" Condition="'$(ConfigurationName)' == 'Release'" />
</Target> </Target>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
</Project> </Project>

@ -2264,13 +2264,6 @@
<PostBuildEvent> <PostBuildEvent>
</PostBuildEvent> </PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- 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.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata> <metadata>
<id>MediaBrowser.Common.Internal</id> <id>MediaBrowser.Common.Internal</id>
<version>3.0.494</version> <version>3.0.496</version>
<title>MediaBrowser.Common.Internal</title> <title>MediaBrowser.Common.Internal</title>
<authors>Luke</authors> <authors>Luke</authors>
<owners>ebr,Luke,scottisafool</owners> <owners>ebr,Luke,scottisafool</owners>
@ -12,7 +12,7 @@
<description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description> <description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Media Browser 2013</copyright> <copyright>Copyright © Media Browser 2013</copyright>
<dependencies> <dependencies>
<dependency id="MediaBrowser.Common" version="3.0.494" /> <dependency id="MediaBrowser.Common" version="3.0.496" />
<dependency id="NLog" version="3.1.0.0" /> <dependency id="NLog" version="3.1.0.0" />
<dependency id="SimpleInjector" version="2.6.0" /> <dependency id="SimpleInjector" version="2.6.0" />
<dependency id="sharpcompress" version="0.10.2" /> <dependency id="sharpcompress" version="0.10.2" />

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata> <metadata>
<id>MediaBrowser.Common</id> <id>MediaBrowser.Common</id>
<version>3.0.494</version> <version>3.0.496</version>
<title>MediaBrowser.Common</title> <title>MediaBrowser.Common</title>
<authors>Media Browser Team</authors> <authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners> <owners>ebr,Luke,scottisafool</owners>

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata> <metadata>
<id>MediaBrowser.Model.Signed</id> <id>MediaBrowser.Model.Signed</id>
<version>3.0.494</version> <version>3.0.496</version>
<title>MediaBrowser.Model - Signed Edition</title> <title>MediaBrowser.Model - Signed Edition</title>
<authors>Media Browser Team</authors> <authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners> <owners>ebr,Luke,scottisafool</owners>

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>MediaBrowser.Server.Core</id> <id>MediaBrowser.Server.Core</id>
<version>3.0.494</version> <version>3.0.496</version>
<title>Media Browser.Server.Core</title> <title>Media Browser.Server.Core</title>
<authors>Media Browser Team</authors> <authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners> <owners>ebr,Luke,scottisafool</owners>
@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Media Browser Server.</description> <description>Contains core components required to build plugins for Media Browser Server.</description>
<copyright>Copyright © Media Browser 2013</copyright> <copyright>Copyright © Media Browser 2013</copyright>
<dependencies> <dependencies>
<dependency id="MediaBrowser.Common" version="3.0.494" /> <dependency id="MediaBrowser.Common" version="3.0.496" />
</dependencies> </dependencies>
</metadata> </metadata>
<files> <files>

Loading…
Cancel
Save