This commit is contained in:
Tavares André 2015-04-30 18:20:10 +02:00
commit fcb2bc2c8e
125 changed files with 1166 additions and 1005 deletions

View file

@ -34,7 +34,7 @@
<ItemGroup>
<Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ImageMagickSharp.1.0.0.15\lib\net45\ImageMagickSharp.dll</HintPath>
<HintPath>..\packages\ImageMagickSharp.1.0.0.16\lib\net45\ImageMagickSharp.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />

View file

@ -63,6 +63,7 @@ namespace Emby.Drawing.ImageMagick
{
_logger.Info("ImageMagick version: " + Wand.VersionString);
TestWebp();
Wand.SetMagickThreadCount(1);
}
private bool _webpAvailable = true;

View file

@ -51,8 +51,14 @@ namespace Emby.Drawing
private readonly IJsonSerializer _jsonSerializer;
private readonly IServerApplicationPaths _appPaths;
private readonly IImageEncoder _imageEncoder;
private readonly SemaphoreSlim _imageProcessingSemaphore;
public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem, IJsonSerializer jsonSerializer, IImageEncoder imageEncoder)
public ImageProcessor(ILogger logger,
IServerApplicationPaths appPaths,
IFileSystem fileSystem,
IJsonSerializer jsonSerializer,
IImageEncoder imageEncoder,
int maxConcurrentImageProcesses)
{
_logger = logger;
_fileSystem = fileSystem;
@ -88,6 +94,8 @@ namespace Emby.Drawing
}
_cachedImagedSizes = new ConcurrentDictionary<Guid, ImageSize>(sizeDictionary);
_logger.Info("ImageProcessor started with {0} max concurrent image processes", maxConcurrentImageProcesses);
_imageProcessingSemaphore = new SemaphoreSlim(maxConcurrentImageProcesses, maxConcurrentImageProcesses);
}
public string[] SupportedInputFormats
@ -201,6 +209,8 @@ namespace Emby.Drawing
await semaphore.WaitAsync().ConfigureAwait(false);
var imageProcessingLockTaken = false;
try
{
CheckDisposed();
@ -212,11 +222,20 @@ namespace Emby.Drawing
Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
imageProcessingLockTaken = true;
_imageEncoder.EncodeImage(originalImagePath, cacheFilePath, newWidth, newHeight, quality, options);
}
}
finally
{
if (imageProcessingLockTaken)
{
_imageProcessingSemaphore.Release();
}
semaphore.Release();
}
@ -254,10 +273,15 @@ namespace Emby.Drawing
return GetResult(croppedImagePath);
}
var imageProcessingLockTaken = false;
try
{
Directory.CreateDirectory(Path.GetDirectoryName(croppedImagePath));
await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
imageProcessingLockTaken = true;
_imageEncoder.CropWhiteSpace(originalImagePath, croppedImagePath);
}
catch (Exception ex)
@ -269,6 +293,11 @@ namespace Emby.Drawing
}
finally
{
if (imageProcessingLockTaken)
{
_imageProcessingSemaphore.Release();
}
semaphore.Release();
}
@ -592,13 +621,25 @@ namespace Emby.Drawing
return enhancedImagePath;
}
var imageProcessingLockTaken = false;
try
{
Directory.CreateDirectory(Path.GetDirectoryName(enhancedImagePath));
await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
imageProcessingLockTaken = true;
await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, enhancedImagePath, item, imageType, imageIndex).ConfigureAwait(false);
}
finally
{
if (imageProcessingLockTaken)
{
_imageProcessingSemaphore.Release();
}
semaphore.Release();
}
@ -717,9 +758,18 @@ namespace Emby.Drawing
return Path.Combine(path, filename);
}
public void CreateImageCollage(ImageCollageOptions options)
public async Task CreateImageCollage(ImageCollageOptions options)
{
_imageEncoder.CreateImageCollage(options);
await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
try
{
_imageEncoder.CreateImageCollage(options);
}
finally
{
_imageProcessingSemaphore.Release();
}
}
public IEnumerable<IImageEnhancer> GetSupportedEnhancers(IHasImages item, ImageType imageType)

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ImageMagickSharp" version="1.0.0.15" targetFramework="net45" />
<package id="ImageMagickSharp" version="1.0.0.16" targetFramework="net45" />
</packages>

View file

@ -1,7 +1,9 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Providers;
using ServiceStack;
using System.Threading;
namespace MediaBrowser.Api
{
@ -52,7 +54,14 @@ namespace MediaBrowser.Api
var options = GetRefreshOptions(request);
_providerManager.QueueRefresh(item.Id, options);
if (item is Folder)
{
_providerManager.QueueRefresh(item.Id, options);
}
else
{
_providerManager.RefreshFullItem(item, options, CancellationToken.None);
}
}
private MetadataRefreshOptions GetRefreshOptions(BaseRefreshRequest request)

View file

@ -591,7 +591,7 @@ namespace MediaBrowser.Api.Library
ThemeSongsResult = themeSongs,
ThemeVideosResult = themeVideos,
SoundtrackSongsResult = GetSoundtrackSongs(request, request.Id, request.UserId, request.InheritFromParent)
SoundtrackSongsResult = new ThemeMediaResult()
});
}
@ -789,53 +789,5 @@ namespace MediaBrowser.Api.Library
return ToOptimizedSerializedResultUsingCache(lookup);
}
public ThemeMediaResult GetSoundtrackSongs(GetThemeMedia request, string id, Guid? userId, bool inheritFromParent)
{
var user = userId.HasValue ? _userManager.GetUserById(userId.Value) : null;
var item = string.IsNullOrEmpty(id)
? (userId.HasValue
? user.RootFolder
: _libraryManager.RootFolder)
: _libraryManager.GetItemById(id);
var dtoOptions = GetDtoOptions(request);
var dtos = GetSoundtrackSongIds(item, inheritFromParent)
.Select(_libraryManager.GetItemById)
.OfType<MusicAlbum>()
.SelectMany(i => i.GetRecursiveChildren(a => a is Audio))
.OrderBy(i => i.SortName)
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
var items = dtos.ToArray();
return new ThemeMediaResult
{
Items = items,
TotalRecordCount = items.Length,
OwnerId = _dtoService.GetDtoId(item)
};
}
private IEnumerable<Guid> GetSoundtrackSongIds(BaseItem item, bool inherit)
{
var hasSoundtracks = item as IHasSoundtracks;
if (hasSoundtracks != null)
{
return hasSoundtracks.SoundtrackIds;
}
if (!inherit)
{
return new List<Guid>();
}
hasSoundtracks = item.Parents.OfType<IHasSoundtracks>().FirstOrDefault();
return hasSoundtracks != null ? hasSoundtracks.SoundtrackIds : new List<Guid>();
}
}
}

View file

@ -62,12 +62,15 @@ namespace MediaBrowser.Api.Movies
public async Task<object> Post(CreateCollection request)
{
var userId = AuthorizationContext.GetAuthorizationInfo(Request).UserId;
var item = await _collectionManager.CreateCollection(new CollectionCreationOptions
{
IsLocked = request.IsLocked,
Name = request.Name,
ParentId = request.ParentId,
ItemIdList = (request.Ids ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => new Guid(i)).ToList()
ItemIdList = (request.Ids ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => new Guid(i)).ToList(),
UserIds = new List<Guid> { new Guid(userId) }
}).ConfigureAwait(false);

View file

@ -1522,6 +1522,10 @@ namespace MediaBrowser.Api.Playback
{
request.LiveStreamId = val;
}
else if (i == 24)
{
// Duplicating ItemId because of MediaMonkey
}
}
}
@ -2029,8 +2033,6 @@ namespace MediaBrowser.Api.Playback
profile.GetVideoMediaProfile(state.OutputContainer,
audioCodec,
videoCodec,
state.OutputAudioBitrate,
state.OutputAudioChannels,
state.OutputWidth,
state.OutputHeight,
state.TargetVideoBitDepth,
@ -2117,8 +2119,6 @@ namespace MediaBrowser.Api.Playback
state.OutputHeight,
state.TargetVideoBitDepth,
state.OutputVideoBitrate,
state.OutputAudioBitrate,
state.OutputAudioChannels,
state.TargetTimestamp,
isStaticallyStreamed,
state.RunTimeTicks,

View file

@ -159,10 +159,12 @@ namespace MediaBrowser.Api.Playback.Hls
{
var text = reader.ReadToEnd();
var newDuration = "#EXT-X-TARGETDURATION:" + segmentLength.ToString(UsCulture) + Environment.NewLine + "#EXT-X-ALLOW-CACHE:NO";
var newDuration = "#EXT-X-TARGETDURATION:" + segmentLength.ToString(UsCulture);
// ffmpeg pads the reported length by a full second
return text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(UsCulture), newDuration, StringComparison.OrdinalIgnoreCase);
text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(UsCulture), newDuration, StringComparison.OrdinalIgnoreCase);
return text;
}
}
}

View file

@ -608,7 +608,6 @@ namespace MediaBrowser.Api.Playback.Hls
builder.AppendLine("#EXT-X-VERSION:3");
builder.AppendLine("#EXT-X-TARGETDURATION:" + state.SegmentLength.ToString(UsCulture));
builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
builder.AppendLine("#EXT-X-ALLOW-CACHE:NO");
var queryStringIndex = Request.RawUrl.IndexOf('?');
var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);

View file

@ -18,16 +18,6 @@ using System.Threading.Tasks;
namespace MediaBrowser.Api.Playback
{
[Route("/Items/{Id}/MediaInfo", "GET", Summary = "Gets live playback media info for an item")]
public class GetLiveMediaInfo : IReturn<PlaybackInfoResponse>
{
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string UserId { get; set; }
}
[Route("/Items/{Id}/PlaybackInfo", "GET", Summary = "Gets live playback media info for an item")]
public class GetPlaybackInfo : IReturn<PlaybackInfoResponse>
{
@ -55,6 +45,19 @@ namespace MediaBrowser.Api.Playback
public string LiveStreamId { get; set; }
}
[Route("/Playback/BitrateTest", "GET")]
public class GetBitrateTestBytes : IReturn<PlaybackInfoResponse>
{
[ApiMember(Name = "Size", Description = "Size", IsRequired = true, DataType = "int", ParameterType = "query", Verb = "GET")]
public long Size { get; set; }
public GetBitrateTestBytes()
{
// 100k
Size = 102400;
}
}
[Authenticated]
public class MediaInfoService : BaseApiService
{
@ -73,13 +76,19 @@ namespace MediaBrowser.Api.Playback
_networkManager = networkManager;
}
public async Task<object> Get(GetPlaybackInfo request)
public object Get(GetBitrateTestBytes request)
{
var result = await GetPlaybackInfo(request.Id, request.UserId, new[] { MediaType.Audio, MediaType.Video }).ConfigureAwait(false);
return ToOptimizedResult(result);
var bytes = new byte[request.Size];
for (var i = 0; i < bytes.Length; i++)
{
bytes[i] = 0;
}
return ResultFactory.GetResult(bytes, "application/octet-stream");
}
public async Task<object> Get(GetLiveMediaInfo request)
public async Task<object> Get(GetPlaybackInfo request)
{
var result = await GetPlaybackInfo(request.Id, request.UserId, new[] { MediaType.Audio, MediaType.Video }).ConfigureAwait(false);
return ToOptimizedResult(result);
@ -325,10 +334,11 @@ namespace MediaBrowser.Api.Playback
private int? GetMaxBitrate(int? clientMaxBitrate)
{
var maxBitrate = clientMaxBitrate;
var remoteClientMaxBitrate = _config.Configuration.RemoteClientBitrateLimit;
if (_config.Configuration.RemoteClientBitrateLimit > 0 && !_networkManager.IsInLocalNetwork(Request.RemoteIp))
if (remoteClientMaxBitrate > 0 && !_networkManager.IsInLocalNetwork(Request.RemoteIp))
{
maxBitrate = Math.Min(maxBitrate ?? _config.Configuration.RemoteClientBitrateLimit, _config.Configuration.RemoteClientBitrateLimit);
maxBitrate = Math.Min(maxBitrate ?? remoteClientMaxBitrate, remoteClientMaxBitrate);
}
return maxBitrate;

View file

@ -66,6 +66,7 @@ namespace MediaBrowser.Api
_config.Configuration.EnableStandaloneMetadata = true;
_config.Configuration.EnableLibraryMetadataSubFolder = true;
_config.Configuration.EnableUserSpecificUserViews = true;
_config.Configuration.EnableCustomPathSubFolders = true;
_config.SaveConfiguration();
}

View file

@ -100,6 +100,7 @@ namespace MediaBrowser.Api.Subtitles
}
[Route("/Videos/{Id}/{MediaSourceId}/Subtitles/{Index}/subtitles.m3u8", "GET", Summary = "Gets an HLS subtitle playlist.")]
[Authenticated]
public class GetSubtitlePlaylist
{
/// <summary>

View file

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Dto;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Sync;
@ -230,6 +231,11 @@ namespace MediaBrowser.Api.Sync
{
var jobItem = _syncManager.GetJobItem(request.Id);
if (jobItem == null)
{
throw new ResourceNotFoundException();
}
if (jobItem.Status < SyncJobItemStatus.ReadyToTransfer)
{
throw new ArgumentException("The job item is not yet ready for transfer.");

View file

@ -58,7 +58,7 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "StudioIds", Description = "Optional. If specified, results will be filtered based on studio. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string StudioIds { get; set; }
/// <summary>
/// Gets or sets the studios.
/// </summary>
@ -68,7 +68,7 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "ArtistIds", Description = "Optional. If specified, results will be filtered based on artist. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string ArtistIds { get; set; }
[ApiMember(Name = "Albums", Description = "Optional. If specified, results will be filtered based on album. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string Albums { get; set; }
@ -622,7 +622,7 @@ namespace MediaBrowser.Api.UserLibrary
private bool ApplyAdditionalFilters(GetItems request, BaseItem i, User user, bool isPreFiltered, ILibraryManager libraryManager)
{
var video = i as Video;
if (!isPreFiltered)
{
var mediaTypes = request.GetMediaTypes();
@ -979,8 +979,8 @@ namespace MediaBrowser.Api.UserLibrary
if (years.Length > 0 && !(i.ProductionYear.HasValue && years.Contains(i.ProductionYear.Value)))
{
return false;
}
}
// Apply person filter
var personIds = request.GetPersonIds();
if (personIds.Length > 0)
@ -1057,7 +1057,7 @@ namespace MediaBrowser.Api.UserLibrary
// Artists
if (!string.IsNullOrEmpty(request.ArtistIds))
{
var artistIds = request.ArtistIds.Split('|');
var artistIds = request.ArtistIds.Split(new[] { '|', ',' });
var audio = i as IHasArtist;

View file

@ -164,9 +164,22 @@ namespace MediaBrowser.Common.Implementations.Configuration
/// </summary>
private void UpdateCachePath()
{
((BaseApplicationPaths)CommonApplicationPaths).CachePath = string.IsNullOrEmpty(CommonConfiguration.CachePath) ?
null :
CommonConfiguration.CachePath;
string cachePath;
if (string.IsNullOrWhiteSpace(CommonConfiguration.CachePath))
{
cachePath = null;
}
else if (CommonConfiguration.EnableCustomPathSubFolders)
{
cachePath = Path.Combine(CommonConfiguration.CachePath, "cache");
}
else
{
cachePath = CommonConfiguration.CachePath;
}
((BaseApplicationPaths)CommonApplicationPaths).CachePath = cachePath;
}
/// <summary>

View file

@ -48,9 +48,9 @@
<RunPostBuildEvent>Always</RunPostBuildEvent>
</PropertyGroup>
<ItemGroup>
<Reference Include="NLog, Version=3.2.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<Reference Include="NLog, Version=3.2.1.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.3.2.0.0\lib\net45\NLog.dll</HintPath>
<HintPath>..\packages\NLog.3.2.1\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="SharpCompress, Version=0.10.2.0, Culture=neutral, PublicKeyToken=beaf6f427e128133, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NLog" version="3.2.0.0" targetFramework="net45" />
<package id="NLog" version="3.2.1" targetFramework="net45" />
<package id="SimpleInjector" version="2.7.0" targetFramework="net45" />
</packages>

View file

@ -0,0 +1,9 @@
using System;
namespace MediaBrowser.Controller.Dlna
{
public interface ISsdpHandler
{
event EventHandler<SsdpMessageEventArgs> MessageReceived;
}
}

View file

@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Net;
namespace MediaBrowser.Dlna.Ssdp
namespace MediaBrowser.Controller.Dlna
{
public class SsdpMessageEventArgs
{
@ -13,6 +13,7 @@ namespace MediaBrowser.Dlna.Ssdp
public Dictionary<string, string> Headers { get; set; }
public IPAddress LocalIp { get; set; }
public byte[] Message { get; set; }
public SsdpMessageEventArgs()
{

View file

@ -104,6 +104,6 @@ namespace MediaBrowser.Controller.Drawing
/// Creates the image collage.
/// </summary>
/// <param name="options">The options.</param>
void CreateImageCollage(ImageCollageOptions options);
Task CreateImageCollage(ImageCollageOptions options);
}
}

View file

@ -17,10 +17,6 @@ namespace MediaBrowser.Controller.Entities.Audio
public static class HasArtistExtensions
{
public static bool HasArtist(this IHasArtist hasArtist, string artist)
{
return NameExtensions.EqualsAny(hasArtist.Artists, artist);
}
public static bool HasAnyArtist(this IHasArtist hasArtist, string artist)
{
return NameExtensions.EqualsAny(hasArtist.AllArtists, artist);

View file

@ -491,6 +491,17 @@ namespace MediaBrowser.Controller.Entities
}
}
/// <summary>
/// Finds a parent of a given type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>``0.</returns>
public T FindParent<T>()
where T : Folder
{
return Parents.OfType<T>().FirstOrDefault();
}
[IgnoreDataMember]
public virtual BaseItem DisplayParent
{
@ -1457,30 +1468,6 @@ namespace MediaBrowser.Controller.Entities
return Task.FromResult(true);
}
/// <summary>
/// Finds a parent of a given type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>``0.</returns>
public T FindParent<T>()
where T : Folder
{
var parent = Parent;
while (parent != null)
{
var result = parent as T;
if (result != null)
{
return result;
}
parent = parent.Parent;
}
return null;
}
/// <summary>
/// Gets an image
/// </summary>

View file

@ -8,10 +8,8 @@ using System.Linq;
namespace MediaBrowser.Controller.Entities
{
public class Game : BaseItem, IHasSoundtracks, IHasTrailers, IHasThemeMedia, IHasTags, IHasScreenshots, ISupportsPlaceHolders, IHasPreferredMetadataLanguage, IHasLookupInfo<GameInfo>
public class Game : BaseItem, IHasTrailers, IHasThemeMedia, IHasTags, IHasScreenshots, ISupportsPlaceHolders, IHasPreferredMetadataLanguage, IHasLookupInfo<GameInfo>
{
public List<Guid> SoundtrackIds { get; set; }
public List<Guid> ThemeSongIds { get; set; }
public List<Guid> ThemeVideoIds { get; set; }
@ -26,7 +24,6 @@ namespace MediaBrowser.Controller.Entities
public Game()
{
MultiPartGameFiles = new List<string>();
SoundtrackIds = new List<Guid>();
RemoteTrailers = new List<MediaUrl>();
LocalTrailerIds = new List<Guid>();
RemoteTrailerIds = new List<Guid>();

View file

@ -1,29 +0,0 @@
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
/// <summary>
/// Interface IHasSoundtracks
/// </summary>
public interface IHasSoundtracks
{
/// <summary>
/// Gets or sets the soundtrack ids.
/// </summary>
/// <value>The soundtrack ids.</value>
List<Guid> SoundtrackIds { get; set; }
/// <summary>
/// Gets the name.
/// </summary>
/// <value>The name.</value>
string Name { get; }
/// <summary>
/// Gets the identifier.
/// </summary>
/// <value>The identifier.</value>
Guid Id { get; }
}
}

View file

@ -14,12 +14,11 @@ namespace MediaBrowser.Controller.Entities.Movies
/// <summary>
/// Class Movie
/// </summary>
public class Movie : Video, IHasCriticRating, IHasSoundtracks, IHasSpecialFeatures, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTrailers, IHasThemeMedia, IHasTaglines, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping, IHasOriginalTitle
public class Movie : Video, IHasCriticRating, IHasSpecialFeatures, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTrailers, IHasThemeMedia, IHasTaglines, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping, IHasOriginalTitle
{
public List<Guid> SpecialFeatureIds { get; set; }
public string OriginalTitle { get; set; }
public List<Guid> SoundtrackIds { get; set; }
public List<Guid> ThemeSongIds { get; set; }
public List<Guid> ThemeVideoIds { get; set; }
@ -28,7 +27,6 @@ namespace MediaBrowser.Controller.Entities.Movies
public Movie()
{
SpecialFeatureIds = new List<Guid>();
SoundtrackIds = new List<Guid>();
RemoteTrailers = new List<MediaUrl>();
LocalTrailerIds = new List<Guid>();
RemoteTrailerIds = new List<Guid>();
@ -102,7 +100,19 @@ namespace MediaBrowser.Controller.Entities.Movies
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{
return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.CreateUserDataKey();
var key = this.GetProviderId(MetadataProviders.Tmdb);
if (string.IsNullOrWhiteSpace(key))
{
key = this.GetProviderId(MetadataProviders.Imdb);
}
if (string.IsNullOrWhiteSpace(key))
{
key = base.CreateUserDataKey();
}
return key;
}
protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)

View file

@ -15,10 +15,9 @@ namespace MediaBrowser.Controller.Entities.TV
/// <summary>
/// Class Series
/// </summary>
public class Series : Folder, IHasSoundtracks, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>, IHasSpecialFeatures, IMetadataContainer, IHasOriginalTitle
public class Series : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>, IHasSpecialFeatures, IMetadataContainer, IHasOriginalTitle
{
public List<Guid> SpecialFeatureIds { get; set; }
public List<Guid> SoundtrackIds { get; set; }
public string OriginalTitle { get; set; }
public int SeasonCount { get; set; }
@ -30,7 +29,6 @@ namespace MediaBrowser.Controller.Entities.TV
AirDays = new List<DayOfWeek>();
SpecialFeatureIds = new List<Guid>();
SoundtrackIds = new List<Guid>();
RemoteTrailers = new List<MediaUrl>();
LocalTrailerIds = new List<Guid>();
RemoteTrailerIds = new List<Guid>();
@ -63,7 +61,7 @@ namespace MediaBrowser.Controller.Entities.TV
/// airdate, dvd or absolute
/// </summary>
public string DisplayOrder { get; set; }
/// <summary>
/// Gets or sets the status.
/// </summary>
@ -115,7 +113,19 @@ namespace MediaBrowser.Controller.Entities.TV
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{
return this.GetProviderId(MetadataProviders.Tvdb) ?? this.GetProviderId(MetadataProviders.Tvcom) ?? base.CreateUserDataKey();
var key = this.GetProviderId(MetadataProviders.Tvdb);
if (string.IsNullOrWhiteSpace(key))
{
key = this.GetProviderId(MetadataProviders.Imdb);
}
if (string.IsNullOrWhiteSpace(key))
{
key = base.CreateUserDataKey();
}
return key;
}
/// <summary>
@ -190,7 +200,7 @@ namespace MediaBrowser.Controller.Entities.TV
public IEnumerable<Episode> GetEpisodes(User user)
{
var config = user.Configuration;
return GetEpisodes(user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
}

View file

@ -14,17 +14,14 @@ namespace MediaBrowser.Controller.Entities
/// Class Trailer
/// </summary>
[Obsolete]
public class Trailer : Video, IHasCriticRating, IHasSoundtracks, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTaglines, IHasMetascore, IHasLookupInfo<TrailerInfo>
public class Trailer : Video, IHasCriticRating, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTaglines, IHasMetascore, IHasLookupInfo<TrailerInfo>
{
public List<Guid> SoundtrackIds { get; set; }
public List<string> ProductionLocations { get; set; }
public Trailer()
{
RemoteTrailers = new List<MediaUrl>();
Taglines = new List<string>();
SoundtrackIds = new List<Guid>();
Keywords = new List<string>();
ProductionLocations = new List<string>();
}

View file

@ -116,7 +116,9 @@
<Compile Include="Dlna\IDlnaManager.cs" />
<Compile Include="Dlna\IEventManager.cs" />
<Compile Include="Dlna\IMediaReceiverRegistrar.cs" />
<Compile Include="Dlna\ISsdpHandler.cs" />
<Compile Include="Dlna\IUpnpService.cs" />
<Compile Include="Dlna\SsdpMessageEventArgs.cs" />
<Compile Include="Drawing\IImageProcessor.cs" />
<Compile Include="Drawing\ImageCollageOptions.cs" />
<Compile Include="Drawing\ImageProcessingOptions.cs" />
@ -150,7 +152,6 @@
<Compile Include="Entities\IHasScreenshots.cs" />
<Compile Include="Entities\IHasSeries.cs" />
<Compile Include="Entities\IHasShortOverview.cs" />
<Compile Include="Entities\IHasSoundtracks.cs" />
<Compile Include="Entities\IHasSpecialFeatures.cs" />
<Compile Include="Entities\IHasTaglines.cs" />
<Compile Include="Entities\IHasTags.cs" />

View file

@ -1426,6 +1426,46 @@ namespace MediaBrowser.Controller.Providers
return null;
}
protected Share GetShare(XmlReader reader)
{
reader.MoveToContent();
var item = new Share();
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "UserId":
{
item.UserId = reader.ReadElementContentAsString();
break;
}
case "CanEdit":
{
item.CanEdit = string.Equals(reader.ReadElementContentAsString(), "true", StringComparison.OrdinalIgnoreCase);
break;
}
default:
reader.Skip();
break;
}
}
}
// This is valid
if (!string.IsNullOrWhiteSpace(item.UserId))
{
return item;
}
return null;
}
/// <summary>
/// Used to split names of comma or pipe delimeted genres and people

View file

@ -2,6 +2,7 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Dlna.ContentDirectory;
using MediaBrowser.Dlna.PlayTo;

View file

@ -638,6 +638,17 @@ namespace MediaBrowser.Dlna.ContentDirectory
Guid itemId;
StubType? stubType = null;
// After using PlayTo, MediaMonkey sends a request to the server trying to get item info
const string paramsSrch = "Params=";
var paramsIndex = id.IndexOf(paramsSrch, StringComparison.OrdinalIgnoreCase);
if (paramsIndex != -1)
{
id = id.Substring(paramsIndex + paramsSrch.Length);
var parts = id.Split(';');
id = parts[24];
}
if (id.StartsWith("folder_", StringComparison.OrdinalIgnoreCase))
{
stubType = StubType.Folder;

View file

@ -149,8 +149,6 @@ namespace MediaBrowser.Dlna.Didl
targetHeight,
streamInfo.TargetVideoBitDepth,
streamInfo.TargetVideoBitrate,
streamInfo.TargetAudioChannels,
streamInfo.TargetAudioBitrate,
streamInfo.TargetTimestamp,
streamInfo.IsDirectStream,
streamInfo.RunTimeTicks,
@ -276,11 +274,9 @@ namespace MediaBrowser.Dlna.Didl
streamInfo.AudioCodec,
streamInfo.VideoCodec,
streamInfo.TargetAudioBitrate,
targetChannels,
targetWidth,
targetHeight,
streamInfo.TargetVideoBitDepth,
streamInfo.TargetVideoBitrate,
streamInfo.TargetVideoProfile,
streamInfo.TargetVideoLevel,
streamInfo.TargetFramerate,

View file

@ -8,7 +8,6 @@ using MediaBrowser.Controller.Plugins;
using MediaBrowser.Dlna.Profiles;
using MediaBrowser.Dlna.Server;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dlna.Profiles;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;

View file

@ -37,13 +37,26 @@ namespace MediaBrowser.Dlna.Main
private readonly ILocalizationManager _localization;
private readonly IMediaSourceManager _mediaSourceManager;
private SsdpHandler _ssdpHandler;
private readonly SsdpHandler _ssdpHandler;
private DeviceDiscovery _deviceDiscovery;
private readonly List<string> _registeredServerIds = new List<string>();
private bool _dlnaServerStarted;
public DlnaEntryPoint(IServerConfigurationManager config, ILogManager logManager, IServerApplicationHost appHost, INetworkManager network, ISessionManager sessionManager, IHttpClient httpClient, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IImageProcessor imageProcessor, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager)
public DlnaEntryPoint(IServerConfigurationManager config,
ILogManager logManager,
IServerApplicationHost appHost,
INetworkManager network,
ISessionManager sessionManager,
IHttpClient httpClient,
ILibraryManager libraryManager,
IUserManager userManager,
IDlnaManager dlnaManager,
IImageProcessor imageProcessor,
IUserDataManager userDataManager,
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
ISsdpHandler ssdpHandler)
{
_config = config;
_appHost = appHost;
@ -57,6 +70,7 @@ namespace MediaBrowser.Dlna.Main
_userDataManager = userDataManager;
_localization = localization;
_mediaSourceManager = mediaSourceManager;
_ssdpHandler = (SsdpHandler)ssdpHandler;
_logger = logManager.GetLogger("Dlna");
}
@ -109,8 +123,6 @@ namespace MediaBrowser.Dlna.Main
{
try
{
_ssdpHandler = new SsdpHandler(_logger, _config, GenerateServerSignature());
_ssdpHandler.Start();
_deviceDiscovery = new DeviceDiscovery(_logger, _config, _ssdpHandler, _appHost);
@ -123,7 +135,7 @@ namespace MediaBrowser.Dlna.Main
}
}
private void DisposeSsdpHandler()
private void DisposeDeviceDiscovery()
{
try
{
@ -133,15 +145,6 @@ namespace MediaBrowser.Dlna.Main
{
_logger.ErrorException("Error disposing device discovery", ex);
}
try
{
_ssdpHandler.Dispose();
}
catch (Exception ex)
{
_logger.ErrorException("Error disposing ssdp handler", ex);
}
}
public void StartDlnaServer()
@ -184,29 +187,6 @@ namespace MediaBrowser.Dlna.Main
}
}
private string GenerateServerSignature()
{
var os = Environment.OSVersion;
var pstring = os.Platform.ToString();
switch (os.Platform)
{
case PlatformID.Win32NT:
case PlatformID.Win32S:
case PlatformID.Win32Windows:
pstring = "WIN";
break;
}
return String.Format(
"{0}{1}/{2}.{3} UPnP/1.0 DLNADOC/1.5 MediaBrowser/{4}",
pstring,
IntPtr.Size * 8,
os.Version.Major,
os.Version.Minor,
_appHost.ApplicationVersion
);
}
private readonly object _syncLock = new object();
private void StartPlayToManager()
{
@ -260,7 +240,7 @@ namespace MediaBrowser.Dlna.Main
{
DisposeDlnaServer();
DisposePlayToManager();
DisposeSsdpHandler();
DisposeDeviceDiscovery();
}
public void DisposeDlnaServer()

View file

@ -77,6 +77,7 @@
<Compile Include="Common\DeviceService.cs" />
<Compile Include="Didl\DidlBuilder.cs" />
<Compile Include="PlayTo\PlayToController.cs" />
<Compile Include="Profiles\DefaultProfile.cs" />
<Compile Include="Profiles\DirectTvProfile.cs" />
<Compile Include="Profiles\DishHopperJoeyProfile.cs" />
<Compile Include="Profiles\PopcornHourProfile.cs" />
@ -134,7 +135,6 @@
<Compile Include="Server\Headers.cs" />
<Compile Include="Server\UpnpDevice.cs" />
<Compile Include="Ssdp\SsdpMessageBuilder.cs" />
<Compile Include="Ssdp\SsdpMessageEventArgs.cs" />
<Compile Include="Ssdp\SsdpHandler.cs" />
</ItemGroup>
<ItemGroup>

View file

@ -310,12 +310,12 @@ namespace MediaBrowser.Dlna.PlayTo
{
if (isFirst && command.StartPositionTicks.HasValue)
{
playlist.Add(CreatePlaylistItem(item, user, command.StartPositionTicks.Value));
playlist.Add(CreatePlaylistItem(item, user, command.StartPositionTicks.Value, null, null, null));
isFirst = false;
}
else
{
playlist.Add(CreatePlaylistItem(item, user, 0));
playlist.Add(CreatePlaylistItem(item, user, 0, null, null, null));
}
}
@ -456,11 +456,6 @@ namespace MediaBrowser.Dlna.PlayTo
}
}
private PlaylistItem CreatePlaylistItem(BaseItem item, User user, long startPostionTicks)
{
return CreatePlaylistItem(item, user, startPostionTicks, null, null, null);
}
private PlaylistItem CreatePlaylistItem(BaseItem item, User user, long startPostionTicks, string mediaSourceId, int? audioStreamIndex, int? subtitleStreamIndex)
{
var deviceInfo = _device.Properties;
@ -514,8 +509,6 @@ namespace MediaBrowser.Dlna.PlayTo
streamInfo.TargetHeight,
streamInfo.TargetVideoBitDepth,
streamInfo.TargetVideoBitrate,
streamInfo.TargetAudioChannels,
streamInfo.TargetAudioBitrate,
streamInfo.TargetTimestamp,
streamInfo.IsDirectStream,
streamInfo.RunTimeTicks,

View file

@ -13,6 +13,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
namespace MediaBrowser.Dlna.PlayTo
{
@ -34,6 +35,9 @@ namespace MediaBrowser.Dlna.PlayTo
private readonly DeviceDiscovery _deviceDiscovery;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly List<string> _nonRendererUrls = new List<string>();
private Timer _clearNonRenderersTimer;
public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, DeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager)
{
_logger = logger;
@ -53,9 +57,19 @@ namespace MediaBrowser.Dlna.PlayTo
public void Start()
{
_clearNonRenderersTimer = new Timer(OnClearUrlTimerCallback, null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
_deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered;
}
private void OnClearUrlTimerCallback(object state)
{
lock (_nonRendererUrls)
{
_nonRendererUrls.Clear();
}
}
async void _deviceDiscovery_DeviceDiscovered(object sender, SsdpMessageEventArgs e)
{
var localIp = e.LocalIp;
@ -68,7 +82,7 @@ namespace MediaBrowser.Dlna.PlayTo
string location;
if (!e.Headers.TryGetValue("Location", out location)) location = string.Empty;
// It has to report that it's a media renderer
if (usn.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1 &&
nt.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1)
@ -85,61 +99,75 @@ namespace MediaBrowser.Dlna.PlayTo
{
var uri = new Uri(location);
lock (_nonRendererUrls)
{
if (_nonRendererUrls.Contains(location, StringComparer.OrdinalIgnoreCase))
{
return;
}
}
var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger).ConfigureAwait(false);
if (device.RendererCommands != null)
if (device.RendererCommands == null)
{
var sessionInfo = await _sessionManager.LogSessionActivity(device.Properties.ClientType, _appHost.ApplicationVersion.ToString(), device.Properties.UUID, device.Properties.Name, uri.OriginalString, null)
.ConfigureAwait(false);
var controller = sessionInfo.SessionController as PlayToController;
if (controller == null)
lock (_nonRendererUrls)
{
var serverAddress = GetServerAddress(localIp);
string accessToken = null;
sessionInfo.SessionController = controller = new PlayToController(sessionInfo,
_sessionManager,
_libraryManager,
_logger,
_dlnaManager,
_userManager,
_imageProcessor,
serverAddress,
accessToken,
_deviceDiscovery,
_userDataManager,
_localization,
_mediaSourceManager);
controller.Init(device);
var profile = _dlnaManager.GetProfile(device.Properties.ToDeviceIdentification()) ??
_dlnaManager.GetDefaultProfile();
_sessionManager.ReportCapabilities(sessionInfo.Id, new ClientCapabilities
{
PlayableMediaTypes = profile.GetSupportedMediaTypes(),
SupportedCommands = new List<string>
{
GeneralCommandType.VolumeDown.ToString(),
GeneralCommandType.VolumeUp.ToString(),
GeneralCommandType.Mute.ToString(),
GeneralCommandType.Unmute.ToString(),
GeneralCommandType.ToggleMute.ToString(),
GeneralCommandType.SetVolume.ToString(),
GeneralCommandType.SetAudioStreamIndex.ToString(),
GeneralCommandType.SetSubtitleStreamIndex.ToString()
},
SupportsMediaControl = true
});
_logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName);
_nonRendererUrls.Add(location);
return;
}
}
var sessionInfo = await _sessionManager.LogSessionActivity(device.Properties.ClientType, _appHost.ApplicationVersion.ToString(), device.Properties.UUID, device.Properties.Name, uri.OriginalString, null)
.ConfigureAwait(false);
var controller = sessionInfo.SessionController as PlayToController;
if (controller == null)
{
var serverAddress = GetServerAddress(localIp);
string accessToken = null;
sessionInfo.SessionController = controller = new PlayToController(sessionInfo,
_sessionManager,
_libraryManager,
_logger,
_dlnaManager,
_userManager,
_imageProcessor,
serverAddress,
accessToken,
_deviceDiscovery,
_userDataManager,
_localization,
_mediaSourceManager);
controller.Init(device);
var profile = _dlnaManager.GetProfile(device.Properties.ToDeviceIdentification()) ??
_dlnaManager.GetDefaultProfile();
_sessionManager.ReportCapabilities(sessionInfo.Id, new ClientCapabilities
{
PlayableMediaTypes = profile.GetSupportedMediaTypes(),
SupportedCommands = new List<string>
{
GeneralCommandType.VolumeDown.ToString(),
GeneralCommandType.VolumeUp.ToString(),
GeneralCommandType.Mute.ToString(),
GeneralCommandType.Unmute.ToString(),
GeneralCommandType.ToggleMute.ToString(),
GeneralCommandType.SetVolume.ToString(),
GeneralCommandType.SetAudioStreamIndex.ToString(),
GeneralCommandType.SetSubtitleStreamIndex.ToString()
},
SupportsMediaControl = true
});
_logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName);
}
}
catch (Exception ex)
{
@ -155,6 +183,12 @@ namespace MediaBrowser.Dlna.PlayTo
public void Dispose()
{
_deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered;
if (_clearNonRenderersTimer != null)
{
_clearNonRenderersTimer.Dispose();
_clearNonRenderersTimer = null;
}
}
}
}

View file

@ -4,8 +4,6 @@ namespace MediaBrowser.Dlna.PlayTo
{
public class PlaylistItem
{
public int PlayState { get; set; }
public string StreamUrl { get; set; }
public string Didl { get; set; }

View file

@ -1,6 +1,7 @@
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
namespace MediaBrowser.Model.Dlna.Profiles
namespace MediaBrowser.Dlna.Profiles
{
[XmlRoot("Profile")]
public class DefaultProfile : DeviceProfile

View file

@ -1,6 +1,5 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna.Profiles;
namespace MediaBrowser.Dlna.Profiles
{

View file

@ -1,6 +1,5 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna.Profiles;
namespace MediaBrowser.Dlna.Profiles
{

View file

@ -1,6 +1,5 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna.Profiles;
namespace MediaBrowser.Dlna.Profiles
{

View file

@ -1,6 +1,5 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna.Profiles;
namespace MediaBrowser.Dlna.Profiles
{

View file

@ -1,6 +1,5 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna.Profiles;
namespace MediaBrowser.Dlna.Profiles
{

View file

@ -1,7 +1,6 @@
using System.Xml.Serialization;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dlna.Profiles;
namespace MediaBrowser.Dlna.Profiles
{

View file

@ -1,6 +1,5 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna.Profiles;
namespace MediaBrowser.Dlna.Profiles
{

View file

@ -1,6 +1,5 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna.Profiles;
namespace MediaBrowser.Dlna.Profiles
{

View file

@ -1,5 +1,4 @@
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dlna.Profiles;
using System.Xml.Serialization;
namespace MediaBrowser.Dlna.Profiles

View file

@ -1,5 +1,4 @@
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dlna.Profiles;
using System.Xml.Serialization;
namespace MediaBrowser.Dlna.Profiles

View file

@ -1,5 +1,4 @@
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dlna.Profiles;
using System.Xml.Serialization;
namespace MediaBrowser.Dlna.Profiles

View file

@ -1,6 +1,5 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna.Profiles;
namespace MediaBrowser.Dlna.Profiles
{

View file

@ -1,6 +1,5 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna.Profiles;
namespace MediaBrowser.Dlna.Profiles
{

View file

@ -1,6 +1,5 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna.Profiles;
namespace MediaBrowser.Dlna.Profiles
{

View file

@ -1,6 +1,5 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna.Profiles;
namespace MediaBrowser.Dlna.Profiles
{

View file

@ -1,6 +1,5 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna.Profiles;
namespace MediaBrowser.Dlna.Profiles
{

View file

@ -1,6 +1,5 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna.Profiles;
namespace MediaBrowser.Dlna.Profiles
{

View file

@ -1,5 +1,4 @@
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dlna.Profiles;
using System.Xml.Serialization;
namespace MediaBrowser.Dlna.Profiles

View file

@ -1,6 +1,5 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna.Profiles;
namespace MediaBrowser.Dlna.Profiles
{

View file

@ -1,5 +1,4 @@
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dlna.Profiles;
using System.Xml.Serialization;
namespace MediaBrowser.Dlna.Profiles

View file

@ -2,6 +2,7 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
@ -47,12 +48,13 @@ namespace MediaBrowser.Dlna.Ssdp
if (!network.SupportsMulticast || OperationalStatus.Up != network.OperationalStatus || !network.GetIPProperties().MulticastAddresses.Any())
continue;
var ipV4 = network.GetIPProperties().GetIPv4Properties();
var properties = network.GetIPProperties();
var ipV4 = properties.GetIPv4Properties();
if (null == ipV4)
continue;
var localIps = network.GetIPProperties().UnicastAddresses
var localIps = properties.UnicastAddresses
.Where(i => i.Address.AddressFamily == AddressFamily.InterNetwork)
.Select(i => i.Address)
.ToList();
@ -182,7 +184,6 @@ namespace MediaBrowser.Dlna.Ssdp
}
}, _tokenSource.Token, TaskCreationOptions.LongRunning);
}
private Socket GetMulticastSocket(IPAddress localIpAddress, EndPoint localEndpoint)

View file

@ -1,6 +1,8 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Dlna.Server;
using MediaBrowser.Model.Logging;
using System;
@ -16,7 +18,7 @@ using System.Threading.Tasks;
namespace MediaBrowser.Dlna.Ssdp
{
public class SsdpHandler : IDisposable
public class SsdpHandler : IDisposable, ISsdpHandler
{
private Socket _socket;
@ -39,13 +41,39 @@ namespace MediaBrowser.Dlna.Ssdp
private bool _isDisposed;
private readonly ConcurrentDictionary<Guid, List<UpnpDevice>> _devices = new ConcurrentDictionary<Guid, List<UpnpDevice>>();
public SsdpHandler(ILogger logger, IServerConfigurationManager config, string serverSignature)
private readonly IApplicationHost _appHost;
public SsdpHandler(ILogger logger, IServerConfigurationManager config, IApplicationHost appHost)
{
_logger = logger;
_config = config;
_serverSignature = serverSignature;
_appHost = appHost;
_config.NamedConfigurationUpdated += _config_ConfigurationUpdated;
_serverSignature = GenerateServerSignature();
}
private string GenerateServerSignature()
{
var os = Environment.OSVersion;
var pstring = os.Platform.ToString();
switch (os.Platform)
{
case PlatformID.Win32NT:
case PlatformID.Win32S:
case PlatformID.Win32Windows:
pstring = "WIN";
break;
}
return String.Format(
"{0}{1}/{2}.{3} UPnP/1.0 DLNADOC/1.5 MediaBrowser/{4}",
pstring,
IntPtr.Size * 8,
os.Version.Major,
os.Version.Minor,
_appHost.ApplicationVersion
);
}
void _config_ConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
@ -116,13 +144,14 @@ namespace MediaBrowser.Dlna.Ssdp
// Seconds to delay response
values["MX"] = "3";
SendDatagram("M-SEARCH * HTTP/1.1", values, localIp);
// UDP is unreliable, so send 3 requests at a time (per Upnp spec, sec 1.1.2)
SendDatagram("M-SEARCH * HTTP/1.1", values, localIp, 2);
}
public void SendDatagram(string header,
Dictionary<string, string> values,
EndPoint localAddress,
int sendCount = 1)
int sendCount)
{
SendDatagram(header, values, _ssdpEndp, localAddress, false, sendCount);
}
@ -132,7 +161,7 @@ namespace MediaBrowser.Dlna.Ssdp
EndPoint endpoint,
EndPoint localAddress,
bool ignoreBindFailure,
int sendCount = 1)
int sendCount)
{
var msg = new SsdpMessageBuilder().BuildMessage(header, values);
var queued = false;
@ -376,11 +405,11 @@ namespace MediaBrowser.Dlna.Ssdp
}
foreach (var d in RegisteredDevices)
{
NotifyDevice(d, "alive");
NotifyDevice(d, "alive", 1);
}
}
private void NotifyDevice(UpnpDevice dev, string type, int sendCount = 1)
private void NotifyDevice(UpnpDevice dev, string type, int sendCount)
{
const string header = "NOTIFY * HTTP/1.1";

View file

@ -1,4 +1,5 @@
using System;
using MediaBrowser.Controller.Dlna;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
@ -34,7 +35,8 @@ namespace MediaBrowser.Dlna.Ssdp
return new SsdpMessageEventArgs
{
Method = method,
Headers = headers
Headers = headers,
Message = data
};
}
}

View file

@ -50,6 +50,14 @@ namespace MediaBrowser.LocalMetadata.Parsers
}
break;
case "Shares":
using (var subReader = reader.ReadSubtree())
{
FetchFromSharesNode(subReader, item);
}
break;
default:
base.FetchDataFromXmlNode(reader, item);
break;
@ -92,5 +100,42 @@ namespace MediaBrowser.LocalMetadata.Parsers
item.LinkedChildren = list;
}
private void FetchFromSharesNode(XmlReader reader, Playlist item)
{
reader.MoveToContent();
var list = new List<Share>();
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "Share":
{
using (var subReader = reader.ReadSubtree())
{
var child = GetShare(subReader);
if (child != null)
{
list.Add(child);
}
}
break;
}
default:
reader.Skip();
break;
}
}
}
item.Shares = list;
}
}
}

View file

@ -716,8 +716,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
profile.GetVideoMediaProfile(outputContainer,
audioCodec,
videoCodec,
state.OutputAudioBitrate,
state.OutputAudioChannels,
state.OutputWidth,
state.OutputHeight,
state.TargetVideoBitDepth,

View file

@ -119,7 +119,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
var extractKeyFrameInterval = request.ExtractKeyFrameInterval && request.Protocol == MediaProtocol.File && request.VideoType == VideoType.VideoFile;
return GetMediaInfoInternal(GetInputArgument(inputFiles, request.Protocol), request.InputPath, request.Protocol, extractChapters, extractKeyFrameInterval,
GetProbeSizeArgument(inputFiles, request.Protocol), request.MediaType == DlnaProfileType.Audio, cancellationToken);
GetProbeSizeArgument(inputFiles, request.Protocol), request.MediaType == DlnaProfileType.Audio, request.VideoType, cancellationToken);
}
/// <summary>
@ -155,6 +155,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <param name="extractKeyFrameInterval">if set to <c>true</c> [extract key frame interval].</param>
/// <param name="probeSizeArgument">The probe size argument.</param>
/// <param name="isAudio">if set to <c>true</c> [is audio].</param>
/// <param name="videoType">Type of the video.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{MediaInfoResult}.</returns>
/// <exception cref="System.ApplicationException"></exception>
@ -165,6 +166,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
bool extractKeyFrameInterval,
string probeSizeArgument,
bool isAudio,
VideoType videoType,
CancellationToken cancellationToken)
{
var args = extractChapters
@ -236,7 +238,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
var mediaInfo = new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, isAudio, primaryPath, protocol);
var mediaInfo = new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol);
if (extractKeyFrameInterval && mediaInfo.RunTimeTicks.HasValue)
{

View file

@ -25,7 +25,7 @@ namespace MediaBrowser.MediaEncoding.Probing
_fileSystem = fileSystem;
}
public Model.MediaInfo.MediaInfo GetMediaInfo(InternalMediaInfoResult data, bool isAudio, string path, MediaProtocol protocol)
public Model.MediaInfo.MediaInfo GetMediaInfo(InternalMediaInfoResult data, VideoType videoType, bool isAudio, string path, MediaProtocol protocol)
{
var info = new Model.MediaInfo.MediaInfo
{
@ -79,7 +79,7 @@ namespace MediaBrowser.MediaEncoding.Probing
var videoStream = info.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
if (videoStream != null)
if (videoStream != null && videoType == VideoType.VideoFile)
{
UpdateFromMediaInfo(info, videoStream);
}
@ -146,7 +146,44 @@ namespace MediaBrowser.MediaEncoding.Probing
// string.Equals(stream.AspectRatio, "2.35:1", StringComparison.OrdinalIgnoreCase) ||
// string.Equals(stream.AspectRatio, "2.40:1", StringComparison.OrdinalIgnoreCase);
stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase);
if (string.Equals(streamInfo.sample_aspect_ratio, "1:1", StringComparison.OrdinalIgnoreCase))
{
stream.IsAnamorphic = false;
}
else if (!((string.IsNullOrWhiteSpace(streamInfo.sample_aspect_ratio) || string.Equals(streamInfo.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase))))
{
stream.IsAnamorphic = true;
}
else if (string.IsNullOrWhiteSpace(streamInfo.display_aspect_ratio) || string.Equals(streamInfo.display_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase))
{
stream.IsAnamorphic = false;
}
else
{
var ratioParts = streamInfo.display_aspect_ratio.Split(':');
if (ratioParts.Length != 2)
{
stream.IsAnamorphic = false;
}
else
{
int ratio0;
int ratio1;
if (!Int32.TryParse(ratioParts[0], NumberStyles.Any, CultureInfo.InvariantCulture, out ratio0))
{
stream.IsAnamorphic = false;
}
else if (!Int32.TryParse(ratioParts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out ratio1))
{
stream.IsAnamorphic = false;
}
else
{
stream.IsAnamorphic = ((streamInfo.width * ratio1) != (stream.Height * ratio0));
}
}
}
}
else
{
@ -863,12 +900,14 @@ namespace MediaBrowser.MediaEncoding.Probing
private void UpdateFromMediaInfo(MediaSourceInfo video, MediaStream videoStream)
{
if (video.VideoType == VideoType.VideoFile && video.Protocol == MediaProtocol.File)
if (video.Protocol == MediaProtocol.File)
{
if (videoStream != null)
{
try
{
_logger.Debug("Running MediaInfo against {0}", video.Path);
var result = new MediaInfoLib().GetVideoInfo(video.Path);
videoStream.IsCabac = result.IsCabac ?? videoStream.IsCabac;

View file

@ -383,9 +383,6 @@
<Compile Include="..\MediaBrowser.Model\Dlna\ProfileConditionValue.cs">
<Link>Dlna\ProfileConditionValue.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\Profiles\DefaultProfile.cs">
<Link>Dlna\Profiles\DefaultProfile.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\ResolutionConfiguration.cs">
<Link>Dlna\ResolutionConfiguration.cs</Link>
</Compile>

View file

@ -348,9 +348,6 @@
<Compile Include="..\MediaBrowser.Model\Dlna\ProfileConditionValue.cs">
<Link>Dlna\ProfileConditionValue.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\Profiles\DefaultProfile.cs">
<Link>Dlna\Profiles\DefaultProfile.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dlna\ResolutionConfiguration.cs">
<Link>Dlna\ResolutionConfiguration.cs</Link>
</Compile>

View file

@ -1499,5 +1499,11 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="itemIds">The item ids.</param>
/// <returns>Task.</returns>
Task CancelSyncLibraryItems(string targetId, IEnumerable<string> itemIds);
/// <summary>
/// Gets the supported bitrate.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;System.Int32&gt;.</returns>
Task<int> GetSupportedBitrate(CancellationToken cancellationToken);
}
}

View file

@ -50,6 +50,12 @@ namespace MediaBrowser.Model.Configuration
/// <value>The cache path.</value>
public string CachePath { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [enable custom path sub folders].
/// </summary>
/// <value><c>true</c> if [enable custom path sub folders]; otherwise, <c>false</c>.</value>
public bool EnableCustomPathSubFolders { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="BaseApplicationConfiguration" /> class.
/// </summary>

View file

@ -17,7 +17,7 @@ namespace MediaBrowser.Model.Configuration
DownMixAudioBoost = 2;
EncodingQuality = EncodingQuality.Auto;
EnableThrottling = true;
ThrottleThresholdSeconds = 90;
ThrottleThresholdSeconds = 120;
}
}
}

View file

@ -185,8 +185,6 @@ namespace MediaBrowser.Model.Configuration
public MetadataOptions[] MetadataOptions { get; set; }
public string TranscodingTempPath { get; set; }
public bool EnableAutomaticRestart { get; set; }
public bool EnableRealtimeMonitor { get; set; }

View file

@ -7,8 +7,6 @@ namespace MediaBrowser.Model.Dlna
public class ConditionProcessor
{
public bool IsVideoConditionSatisfied(ProfileCondition condition,
int? audioBitrate,
int? audioChannels,
int? width,
int? height,
int? bitDepth,
@ -44,10 +42,6 @@ namespace MediaBrowser.Model.Dlna
return IsConditionSatisfied(condition, videoProfile);
case ProfileConditionValue.PacketLength:
return IsConditionSatisfied(condition, packetLength);
case ProfileConditionValue.AudioBitrate:
return IsConditionSatisfied(condition, audioBitrate);
case ProfileConditionValue.AudioChannels:
return IsConditionSatisfied(condition, audioChannels);
case ProfileConditionValue.VideoBitDepth:
return IsConditionSatisfied(condition, bitDepth);
case ProfileConditionValue.VideoBitrate:

View file

@ -105,8 +105,6 @@ namespace MediaBrowser.Model.Dlna
int? height,
int? bitDepth,
int? videoBitrate,
int? audioChannels,
int? audioBitrate,
TransportStreamTimestamp timestamp,
bool isDirectStream,
long? runtimeTicks,
@ -147,8 +145,6 @@ namespace MediaBrowser.Model.Dlna
ResponseProfile mediaProfile = _profile.GetVideoMediaProfile(container,
audioCodec,
videoCodec,
audioBitrate,
audioChannels,
width,
height,
bitDepth,

View file

@ -111,6 +111,10 @@ namespace MediaBrowser.Model.Dlna
XmlRootAttributes = new XmlAttribute[] { };
SupportedMediaTypes = "Audio,Photo,Video";
MaxStreamingBitrate = 8000000;
MaxStaticBitrate = 8000000;
MusicStreamingTranscodingBitrate = 128000;
MusicSyncBitrate = 128000;
}
public List<string> GetSupportedMediaTypes()
@ -268,8 +272,6 @@ namespace MediaBrowser.Model.Dlna
public ResponseProfile GetVideoMediaProfile(string container,
string audioCodec,
string videoCodec,
int? audioBitrate,
int? audioChannels,
int? width,
int? height,
int? bitDepth,
@ -317,7 +319,7 @@ namespace MediaBrowser.Model.Dlna
var anyOff = false;
foreach (ProfileCondition c in i.Conditions)
{
if (!conditionProcessor.IsVideoConditionSatisfied(c, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams))
if (!conditionProcessor.IsVideoConditionSatisfied(c, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams))
{
anyOff = true;
break;

View file

@ -456,7 +456,7 @@ namespace MediaBrowser.Model.Dlna
playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue);
}
int audioBitrate = GetAudioBitrate(playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec);
int audioBitrate = GetAudioBitrate(options.GetMaxBitrate(), playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec, audioStream);
playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate);
int? maxBitrateSetting = options.GetMaxBitrate();
@ -479,17 +479,35 @@ namespace MediaBrowser.Model.Dlna
return playlistItem;
}
private int GetAudioBitrate(int? channels, string codec)
private int GetAudioBitrate(int? maxTotalBitrate, int? targetAudioChannels, string targetAudioCodec, MediaStream audioStream)
{
if (channels.HasValue)
var defaultBitrate = 128000;
if (targetAudioChannels.HasValue)
{
if (channels.Value >= 5)
if (targetAudioChannels.Value >= 5 && (maxTotalBitrate ?? 0) >= 1500000)
{
return 320000;
defaultBitrate = 320000;
}
}
return 128000;
int encoderAudioBitrateLimit = int.MaxValue;
if (audioStream != null)
{
// Seeing webm encoding failures when source has 1 audio channel and 22k bitrate.
// Any attempts to transcode over 64k will fail
if (audioStream.Channels.HasValue &&
audioStream.Channels.Value == 1)
{
if ((audioStream.BitRate ?? 0) < 64000)
{
encoderAudioBitrateLimit = 64000;
}
}
}
return Math.Min(defaultBitrate, encoderAudioBitrateLimit);
}
private PlayMethod? GetVideoDirectPlayProfile(DeviceProfile profile,
@ -560,7 +578,7 @@ namespace MediaBrowser.Model.Dlna
// Check container conditions
foreach (ProfileCondition i in conditions)
{
if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams))
if (!conditionProcessor.IsVideoConditionSatisfied(i, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams))
{
LogConditionFailure(profile, "VideoContainerProfile", i, mediaSource);
@ -593,7 +611,7 @@ namespace MediaBrowser.Model.Dlna
foreach (ProfileCondition i in conditions)
{
if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams))
if (!conditionProcessor.IsVideoConditionSatisfied(i, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams))
{
LogConditionFailure(profile, "VideoCodecProfile", i, mediaSource);

View file

@ -233,6 +233,11 @@ namespace MediaBrowser.Model.Dlna
string liveStreamId = item.MediaSource == null ? null : item.MediaSource.LiveStreamId;
list.Add(new NameValuePair("LiveStreamId", liveStreamId ?? string.Empty));
if (isDlna)
{
list.Add(new NameValuePair("ItemId", item.ItemId));
}
return list;
}

View file

@ -130,7 +130,6 @@
<Compile Include="Dlna\NullLocalPlayer.cs" />
<Compile Include="Dlna\PlaybackErrorCode.cs" />
<Compile Include="Dlna\PlaybackException.cs" />
<Compile Include="Dlna\Profiles\DefaultProfile.cs" />
<Compile Include="Dlna\ResolutionConfiguration.cs" />
<Compile Include="Dlna\ResolutionNormalizer.cs" />
<Compile Include="Dlna\ResolutionOptions.cs" />

View file

@ -199,11 +199,6 @@
/// The series studio
/// </summary>
SeriesStudio,
/// <summary>
/// The soundtrack ids
/// </summary>
SoundtrackIds,
/// <summary>
/// The sort name of the item

View file

@ -38,10 +38,6 @@ namespace MediaBrowser.Providers.BoxSets
list.AddRange(target.LinkedChildren.Where(i => i.Type == LinkedChildType.Manual));
target.LinkedChildren = list;
}
if (replaceData || target.Shares.Count == 0)
{
target.Shares = source.Shares;
}
}

View file

@ -360,7 +360,7 @@ namespace MediaBrowser.Providers.Manager
// If replacing all metadata, run internet providers first
if (options.ReplaceAllMetadata)
{
var remoteResult = await ExecuteRemoteProviders(item, temp, logName, id, providers.OfType<IRemoteMetadataProvider<TItemType, TIdType>>(), cancellationToken)
var remoteResult = await ExecuteRemoteProviders(temp, logName, id, providers.OfType<IRemoteMetadataProvider<TItemType, TIdType>>(), cancellationToken)
.ConfigureAwait(false);
refreshResult.UpdateType = refreshResult.UpdateType | remoteResult.UpdateType;
@ -372,9 +372,8 @@ namespace MediaBrowser.Providers.Manager
var hasLocalMetadata = false;
var userDataList = new List<UserItemData>();
var localProviders = providers.OfType<ILocalMetadataProvider<TItemType>>().ToList();
foreach (var provider in localProviders)
foreach (var provider in providers.OfType<ILocalMetadataProvider<TItemType>>().ToList())
{
var providerName = provider.GetType().Name;
Logger.Debug("Running {0} for {1}", providerName, logName);
@ -433,7 +432,7 @@ namespace MediaBrowser.Providers.Manager
// Local metadata is king - if any is found don't run remote providers
if (!options.ReplaceAllMetadata && (!hasLocalMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh))
{
var remoteResult = await ExecuteRemoteProviders(item, temp, logName, id, providers.OfType<IRemoteMetadataProvider<TItemType, TIdType>>(), cancellationToken)
var remoteResult = await ExecuteRemoteProviders(temp, logName, id, providers.OfType<IRemoteMetadataProvider<TItemType, TIdType>>(), cancellationToken)
.ConfigureAwait(false);
refreshResult.UpdateType = refreshResult.UpdateType | remoteResult.UpdateType;
@ -447,17 +446,15 @@ namespace MediaBrowser.Providers.Manager
if (providers.Any(i => !(i is ICustomMetadataProvider)))
{
// If no local providers and doing a full refresh, take data from item itself
if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh &&
localProviders.Count == 0 &&
refreshResult.UpdateType > ItemUpdateType.None)
{
// TODO: If the new metadata from above has some blank data, this can cause old data to get filled into those empty fields
MergeData(item, temp, new List<MetadataFields>(), false, true);
}
if (refreshResult.UpdateType > ItemUpdateType.None)
{
// If no local metadata, take data from item itself
if (!hasLocalMetadata)
{
// TODO: If the new metadata from above has some blank data, this can cause old data to get filled into those empty fields
MergeData(item, temp, new List<MetadataFields>(), false, true);
}
MergeData(temp, item, item.LockedFields, true, true);
}
}
@ -529,7 +526,7 @@ namespace MediaBrowser.Providers.Manager
return new TItemType();
}
private async Task<RefreshResult> ExecuteRemoteProviders(TItemType item, TItemType temp, string logName, TIdType id, IEnumerable<IRemoteMetadataProvider<TItemType, TIdType>> providers, CancellationToken cancellationToken)
private async Task<RefreshResult> ExecuteRemoteProviders(TItemType temp, string logName, TIdType id, IEnumerable<IRemoteMetadataProvider<TItemType, TIdType>> providers, CancellationToken cancellationToken)
{
var refreshResult = new RefreshResult();

View file

@ -122,7 +122,7 @@ namespace MediaBrowser.Providers.Music
if (singleResult != null)
{
musicBrainzId = singleResult.GetProviderId(MetadataProviders.MusicBrainzArtist);
result.Item.Name = singleResult.Name;
//result.Item.Name = singleResult.Name;
}
}

View file

@ -33,14 +33,10 @@ namespace MediaBrowser.Providers.Playlists
target.PlaylistMediaType = source.PlaylistMediaType;
}
if (replaceData || target.Shares.Count == 0)
{
target.Shares = source.Shares;
}
if (mergeMetadataSettings)
{
target.LinkedChildren = source.LinkedChildren;
target.Shares = source.Shares;
}
}
}

View file

@ -81,7 +81,8 @@ namespace MediaBrowser.Server.Implementations.Collections
ProviderIds = options.ProviderIds,
Shares = options.UserIds.Select(i => new Share
{
UserId = i.ToString("N")
UserId = i.ToString("N"),
CanEdit = true
}).ToList()
};

View file

@ -101,9 +101,22 @@ namespace MediaBrowser.Server.Implementations.Configuration
/// </summary>
private void UpdateMetadataPath()
{
((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = string.IsNullOrEmpty(Configuration.MetadataPath) ?
GetInternalMetadataPath() :
Configuration.MetadataPath;
string metadataPath;
if (string.IsNullOrWhiteSpace(Configuration.MetadataPath))
{
metadataPath = GetInternalMetadataPath();
}
else if (Configuration.EnableCustomPathSubFolders)
{
metadataPath = Path.Combine(Configuration.MetadataPath, "metadata");
}
else
{
metadataPath = Configuration.MetadataPath;
}
((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = metadataPath;
if (Configuration.MergeMetadataAndImagesByName)
{
@ -130,7 +143,7 @@ namespace MediaBrowser.Server.Implementations.Configuration
((ServerApplicationPaths)ApplicationPaths).TranscodingTempPath = string.IsNullOrEmpty(encodingConfig.TranscodingTempPath) ?
null :
encodingConfig.TranscodingTempPath;
Path.Combine(encodingConfig.TranscodingTempPath, "transcoding-temp");
}
protected override void OnNamedConfigurationUpdated(string key, object configuration)

View file

@ -325,18 +325,6 @@ namespace MediaBrowser.Server.Implementations.Dto
AttachBasicFields(dto, item, owner, options);
if (fields.Contains(ItemFields.SoundtrackIds))
{
var hasSoundtracks = item as IHasSoundtracks;
if (hasSoundtracks != null)
{
dto.SoundtrackIds = hasSoundtracks.SoundtrackIds
.Select(i => i.ToString("N"))
.ToArray();
}
}
var playlist = item as Playlist;
if (playlist != null)
{

View file

@ -1,5 +1,6 @@
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Logging;
using Mono.Nat;
@ -7,6 +8,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
@ -17,15 +19,17 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
private readonly IServerApplicationHost _appHost;
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
private readonly ISsdpHandler _ssdp;
private Timer _timer;
private bool _isStarted;
public ExternalPortForwarding(ILogManager logmanager, IServerApplicationHost appHost, IServerConfigurationManager config)
public ExternalPortForwarding(ILogManager logmanager, IServerApplicationHost appHost, IServerConfigurationManager config, ISsdpHandler ssdp)
{
_logger = logmanager.GetLogger("PortMapper");
_appHost = appHost;
_config = config;
_ssdp = ssdp;
}
private string _lastConfigIdentifier;
@ -75,7 +79,10 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
private void Start()
{
_logger.Debug("Starting NAT discovery");
NatUtility.EnabledProtocols = new List<NatProtocol>
{
NatProtocol.Pmp
};
NatUtility.DeviceFound += NatUtility_DeviceFound;
// Mono.Nat does never rise this event. The event is there however it is useless.
@ -88,26 +95,38 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
NatUtility.UnhandledException += NatUtility_UnhandledException;
NatUtility.StartDiscovery();
_timer = new Timer(s => _createdRules = new List<string>(), null, TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(3));
_timer = new Timer(s => _createdRules = new List<string>(), null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
_ssdp.MessageReceived += _ssdp_MessageReceived;
_lastConfigIdentifier = GetConfigIdentifier();
_isStarted = true;
}
void _ssdp_MessageReceived(object sender, SsdpMessageEventArgs e)
{
var endpoint = e.EndPoint as IPEndPoint;
if (endpoint != null && e.LocalIp != null)
{
NatUtility.Handle(e.LocalIp, e.Message, endpoint, NatProtocol.Upnp);
}
}
void NatUtility_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
//var ex = e.ExceptionObject as Exception;
var ex = e.ExceptionObject as Exception;
//if (ex == null)
//{
// _logger.Error("Unidentified error reported by Mono.Nat");
//}
//else
//{
// // Seeing some blank exceptions coming through here
// _logger.ErrorException("Error reported by Mono.Nat: ", ex);
//}
if (ex == null)
{
//_logger.Error("Unidentified error reported by Mono.Nat");
}
else
{
// Seeing some blank exceptions coming through here
//_logger.ErrorException("Error reported by Mono.Nat: ", ex);
}
}
void NatUtility_DeviceFound(object sender, DeviceEventArgs e)
@ -180,6 +199,8 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
_timer = null;
}
_ssdp.MessageReceived -= _ssdp_MessageReceived;
try
{
// This is not a significant improvement

View file

@ -8,7 +8,6 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.FileOrganization;
using MediaBrowser.Model.Logging;
using MediaBrowser.Naming.IO;
using MediaBrowser.Server.Implementations.Library;
using MediaBrowser.Server.Implementations.Logging;
using System;
@ -60,7 +59,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
var namingOptions = ((LibraryManager) _libraryManager).GetNamingOptions();
var resolver = new Naming.TV.EpisodeResolver(namingOptions, new PatternsLogger());
var episodeInfo = resolver.Resolve(path, FileInfoType.File) ??
var episodeInfo = resolver.Resolve(path, false) ??
new Naming.TV.EpisodeInfo();
var seriesName = episodeInfo.SeriesName;

View file

@ -1,4 +1,5 @@
using MediaBrowser.Common.Extensions;
using Interfaces.IO;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Progress;
using MediaBrowser.Common.ScheduledTasks;
@ -17,7 +18,6 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Naming.Audio;
using MediaBrowser.Naming.Common;
using MediaBrowser.Naming.IO;
using MediaBrowser.Naming.TV;
using MediaBrowser.Naming.Video;
using MediaBrowser.Server.Implementations.Library.Validators;
@ -1767,14 +1767,13 @@ namespace MediaBrowser.Server.Implementations.Library
var resolver = new EpisodeResolver(GetNamingOptions(),
new PatternsLogger());
var fileType = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd || episode.VideoType == VideoType.HdDvd ?
FileInfoType.Directory :
FileInfoType.File;
var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd ||
episode.VideoType == VideoType.HdDvd;
var locationType = episode.LocationType;
var episodeInfo = locationType == LocationType.FileSystem || locationType == LocationType.Offline ?
resolver.Resolve(episode.Path, fileType) :
resolver.Resolve(episode.Path, isFolder) :
new Naming.TV.EpisodeInfo();
if (episodeInfo == null)
@ -1928,10 +1927,10 @@ namespace MediaBrowser.Server.Implementations.Library
var videoListResolver = new VideoListResolver(GetNamingOptions(), new PatternsLogger());
var videos = videoListResolver.Resolve(fileSystemChildren.Select(i => new PortableFileInfo
var videos = videoListResolver.Resolve(fileSystemChildren.Select(i => new FileMetadata
{
FullName = i.FullName,
Type = GetFileType(i)
Id = i.FullName,
IsFolder = ((i.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
}).ToList());
@ -1962,16 +1961,6 @@ namespace MediaBrowser.Server.Implementations.Library
}).OrderBy(i => i.Path).ToList();
}
private FileInfoType GetFileType(FileSystemInfo info)
{
if ((info.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
{
return FileInfoType.Directory;
}
return FileInfoType.File;
}
public IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
{
var files = fileSystemChildren.OfType<DirectoryInfo>()
@ -1981,10 +1970,10 @@ namespace MediaBrowser.Server.Implementations.Library
var videoListResolver = new VideoListResolver(GetNamingOptions(), new PatternsLogger());
var videos = videoListResolver.Resolve(fileSystemChildren.Select(i => new PortableFileInfo
var videos = videoListResolver.Resolve(fileSystemChildren.Select(i => new FileMetadata
{
FullName = i.FullName,
Type = GetFileType(i)
Id = i.FullName,
IsFolder = ((i.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
}).ToList());

View file

@ -34,7 +34,7 @@ namespace MediaBrowser.Server.Implementations.Library
var genres = user.RootFolder
.GetRecursiveChildren(user, i => i is Audio)
.Cast<Audio>()
.Where(i => i.HasAnyArtist(name))
.Where(i => i.HasAnyArtist(artist.Name))
.SelectMany(i => i.Genres)
.Concat(artist.Genres)
.Distinct(StringComparer.OrdinalIgnoreCase);
@ -49,7 +49,7 @@ namespace MediaBrowser.Server.Implementations.Library
.Cast<Audio>()
.SelectMany(i => i.Genres)
.Concat(item.Genres)
.Distinct(StringComparer.OrdinalIgnoreCase);
.DistinctNames();
return GetInstantMixFromGenres(genres, user);
}
@ -61,7 +61,7 @@ namespace MediaBrowser.Server.Implementations.Library
.Cast<Audio>()
.SelectMany(i => i.Genres)
.Concat(item.Genres)
.Distinct(StringComparer.OrdinalIgnoreCase);
.DistinctNames();
return GetInstantMixFromGenres(genres, user);
}

View file

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Entities;
using Interfaces.IO;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@ -6,7 +7,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Naming.IO;
using MediaBrowser.Naming.Video;
using MediaBrowser.Server.Implementations.Logging;
using System;
@ -118,10 +118,10 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
var resolver = new VideoListResolver(namingOptions, new PatternsLogger());
var resolverResult = resolver.Resolve(files.Select(i => new PortableFileInfo
var resolverResult = resolver.Resolve(files.Select(i => new FileMetadata
{
FullName = i.FullName,
Type = FileInfoType.File
Id = i.FullName,
IsFolder = false
}).ToList(), suppportMultiEditions).ToList();

View file

@ -6,7 +6,6 @@ using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Naming.Common;
using MediaBrowser.Naming.IO;
using MediaBrowser.Naming.TV;
using MediaBrowser.Server.Implementations.Logging;
using System;
@ -153,7 +152,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
}
var episodeResolver = new Naming.TV.EpisodeResolver(namingOptions, new PatternsLogger());
var episodeInfo = episodeResolver.Resolve(fullName, FileInfoType.File, false);
var episodeInfo = episodeResolver.Resolve(fullName, false, false);
if (episodeInfo != null && episodeInfo.EpisodeNumber.HasValue)
{
return true;

View file

@ -345,7 +345,7 @@ namespace MediaBrowser.Server.Implementations.Library
{
var name = MakeValidUsername(Environment.UserName);
var user = InstantiateNewUser(name, false);
var user = InstantiateNewUser(name);
user.DateLastSaved = DateTime.UtcNow;
@ -552,7 +552,7 @@ namespace MediaBrowser.Server.Implementations.Library
try
{
var user = InstantiateNewUser(name, true);
var user = InstantiateNewUser(name);
var list = Users.ToList();
list.Add(user);
@ -697,21 +697,13 @@ namespace MediaBrowser.Server.Implementations.Library
/// Instantiates the new user.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="checkId">if set to <c>true</c> [check identifier].</param>
/// <returns>User.</returns>
private User InstantiateNewUser(string name, bool checkId)
private User InstantiateNewUser(string name)
{
var id = ("MBUser" + name).GetMD5();
if (checkId && Users.Select(i => i.Id).Contains(id))
{
id = Guid.NewGuid();
}
return new User
{
Name = name,
Id = id,
Id = Guid.NewGuid(),
DateCreated = DateTime.UtcNow,
DateModified = DateTime.UtcNow,
UsesIdForConfigurationPath = true

View file

@ -1040,7 +1040,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
innerProgress.RegisterAction(p => progress.Report(90 + (p * .1)));
await CleanDatabaseInternal(progress, cancellationToken).ConfigureAwait(false);
RefreshIfNeeded(GetPrograms().Where(i => (i.StartDate - DateTime.UtcNow).TotalDays <= 1).ToList());
RefreshIfNeeded(GetPrograms().ToList());
}
private async Task RefreshChannelsInternal(IProgress<double> progress, CancellationToken cancellationToken)

View file

@ -440,6 +440,7 @@
"HeaderVideo": "Video",
"HeaderRuntime": "Runtime",
"HeaderCommunityRating": "Community rating",
"HeaderPasswordReset": "Password Reset",
"HeaderParentalRating": "Parental rating",
"HeaderReleaseDate": "Release date",
"HeaderDateAdded": "Date added",

View file

@ -602,17 +602,17 @@
"ValueArtist": "\u041e\u0440\u044b\u043d\u0434\u0430\u0443\u0448\u044b: {0}",
"ValueArtists": "\u041e\u0440\u044b\u043d\u0434\u0430\u0443\u0448\u044b\u043b\u0430\u0440: {0}",
"HeaderTags": "\u0422\u0435\u0433\u0442\u0435\u0440",
"MediaInfoCameraMake": "\u041a\u0430\u043c\u0435\u0440\u0430 \u04e9\u043d\u0434\u0456\u0440\u0443\u0448\u0456\u0441\u0456",
"MediaInfoCameraMake": "\u041a\u0430\u043c\u0435\u0440\u0430 \u04e9\u043d\u0434-\u0441\u0456",
"MediaInfoCameraModel": "\u041a\u0430\u043c\u0435\u0440\u0430 \u043c\u043e\u0434\u0435\u043b\u0456",
"MediaInfoAltitude": "\u0411\u0438\u0456\u043a\u0442\u0456\u0433\u0456",
"MediaInfoAperture": "\u0414\u0438\u0430\u0444\u0440\u0430\u0433\u043c\u0430\u0441\u044b",
"MediaInfoExposureTime": "\u042d\u043a\u0441\u043f\u043e\u0437\u0438\u0446\u0438\u044f \u0443\u0430\u049b\u044b\u0442\u044b",
"MediaInfoFocalLength": "\u0422\u043e\u0493\u044b\u0441 \u049b\u0430\u0448\u044b\u049b\u0442\u044b\u0493\u044b",
"MediaInfoExposureTime": "\u042d\u043a\u0441\u043f-\u0446\u0438\u044f \u0443\u0430\u049b\u044b\u0442\u044b",
"MediaInfoFocalLength": "\u0422\u043e\u0493\u044b\u0441 \u049b\u0430\u0448-\u0493\u044b",
"MediaInfoOrientation": "\u0411\u0430\u0493\u0434\u0430\u0440\u044b",
"MediaInfoIsoSpeedRating": "ISO \u0436\u0430\u0440\u044b\u049b\u0442\u0430\u043d \u049b\u043e\u0440\u044b\u043d\u0493\u044b\u0448\u0442\u044b\u0493\u044b",
"MediaInfoIsoSpeedRating": "ISO \u0436\u0430\u0440. \u049b\u043e\u0440\u044b\u043d-\u0493\u044b",
"MediaInfoLatitude": "\u0415\u043d\u0434\u0456\u0433\u0456",
"MediaInfoLongitude": "\u0411\u043e\u0439\u043b\u044b\u0493\u044b",
"MediaInfoShutterSpeed": "\u0411\u0435\u043a\u0456\u0442\u043f\u0435 \u0436\u044b\u043b\u0434\u0430\u043c\u0434\u044b\u0493\u044b",
"MediaInfoShutterSpeed": "\u041e\u0431\u0442-\u0440 \u0436\u044b\u043b\u0434-\u0493\u044b",
"MediaInfoSoftware": "\u0411\u0430\u0493\u0434\u0430\u0440\u043b\u0430\u043c\u0430\u0441\u044b",
"HeaderIfYouLikeCheckTheseOut": "\u0415\u0433\u0435\u0440 {0} \u04b1\u043d\u0430\u0441\u0430, \u0431\u04b1\u043b\u0430\u0440\u0434\u044b \u0431\u0430\u0439\u049b\u0430\u04a3\u044b\u0437...",
"HeaderPlotKeywords": "\u0421\u044e\u0436\u0435\u0442\u0442\u0456\u043d \u043a\u0456\u043b\u0442 \u0441\u04e9\u0437\u0434\u0435\u0440\u0456",
@ -636,21 +636,21 @@
"MediaInfoForced": "\u041c\u04d9\u0436\u0431\u04af\u0440\u043b\u0456",
"MediaInfoExternal": "\u0421\u044b\u0440\u0442\u049b\u044b",
"MediaInfoTimestamp": "\u0423\u0430\u049b\u044b\u0442 \u0431\u0435\u043b\u0433\u0456\u0441\u0456",
"MediaInfoPixelFormat": "\u041f\u0438\u043a\u0441\u0435\u043b \u043f\u0456\u0448\u0456\u043c\u0456",
"MediaInfoBitDepth": "\u0411\u0438\u0442\u0442\u0456\u043a \u0442\u0435\u0440\u0435\u04a3\u0434\u0456\u0433\u0456",
"MediaInfoPixelFormat": "\u041f\u0438\u043a\u0441. \u043f\u0456\u0448\u0456\u043c\u0456",
"MediaInfoBitDepth": "\u0422\u04af\u0441 \u0442\u0435\u0440\u0435\u04a3\u0434\u0456\u0433\u0456",
"MediaInfoSampleRate": "\u04ae\u043b\u0433\u0456 \u0436\u0438\u0456\u043b\u0456\u0433\u0456",
"MediaInfoBitrate": "\u049a\u0430\u0440\u049b\u044b\u043d\u044b",
"MediaInfoChannels": "\u0410\u0440\u043d\u0430\u043b\u0430\u0440\u044b",
"MediaInfoLayout": "\u049a\u04b1\u0440\u044b\u043b\u044b\u043c\u044b",
"MediaInfoLayout": "\u0416\u0430\u0439\u043b\u0430\u0441\u0442\u044b\u0440\u0443\u044b",
"MediaInfoLanguage": "\u0422\u0456\u043b\u0456",
"MediaInfoCodec": "\u041a\u043e\u0434\u0435\u043a",
"MediaInfoProfile": "\u041f\u0440\u043e\u0444\u0430\u0439\u043b\u044b",
"MediaInfoLevel": "\u0414\u0435\u04a3\u0433\u0435\u0439\u0456",
"MediaInfoAspectRatio": "\u041f\u0456\u0448\u0456\u043c\u0434\u0456\u043a \u0430\u0440\u0430\u049b\u0430\u0442\u044b\u043d\u0430\u0441\u044b",
"MediaInfoResolution": "\u0410\u0436\u044b\u0440\u0430\u0442\u044b\u043b\u044b\u043c\u0434\u044b\u0493\u044b",
"MediaInfoAnamorphic": "\u0410\u043d\u0430\u043c\u043e\u0440\u0444\u0442\u044b",
"MediaInfoInterlaced": "\u041a\u0435\u0437\u0435\u043a\u0442\u0435\u0441\u0435\u0442\u0456\u043d",
"MediaInfoFramerate": "\u041a\u0430\u0434\u0440 \u0436\u044b\u043b\u0434\u0430\u043c\u0434\u044b\u0493\u044b:",
"MediaInfoAnamorphic": "\u0410\u043d\u0430\u043c\u043e\u0440\u0444\u0442\u044b\u049b",
"MediaInfoInterlaced": "\u041a\u0435\u0437\u0435\u043a\u0442\u0435\u0441\u0443\u043b\u0456\u043a",
"MediaInfoFramerate": "\u041a\u0430\u0434\u0440 \u0436\u044b\u043b\u0434-\u0493\u044b",
"MediaInfoStreamTypeAudio": "\u0414\u044b\u0431\u044b\u0441",
"MediaInfoStreamTypeData": "\u0414\u0435\u0440\u0435\u043a\u0442\u0435\u0440",
"MediaInfoStreamTypeVideo": "\u0411\u0435\u0439\u043d\u0435",

View file

@ -123,7 +123,7 @@
"LabelFree": "Gratis",
"HeaderPlaybackError": "Afspeel Fout",
"MessagePlaybackErrorNotAllowed": "U bent niet bevoegd om deze content af te spelen. Neem contact op met uw systeembeheerder voor meer informatie.",
"MessagePlaybackErrorNoCompatibleStream": "Geen compatibele streams beschikbaar. Probeer het later opnieuw.",
"MessagePlaybackErrorNoCompatibleStream": "Geen compatibele streams beschikbaar. Probeer het later opnieuw of neem contact op met de serverbeheerder.",
"MessagePlaybackErrorRateLimitExceeded": "Je afspeel rate limiet is overschreden. Neem contact op met de beheerder van de server voor details.",
"MessagePlaybackErrorPlaceHolder": "De gekozen content is niet af te spelen vanaf dit apparaat.",
"HeaderSelectAudio": "Selecteer Audio",

View file

@ -119,7 +119,7 @@
"MessageDeleteTaskTrigger": "Deseja realmente excluir este disparador de tarefa?",
"MessageNoPluginsInstalled": "Voc\u00ea n\u00e3o possui plugins instalados.",
"LabelVersionInstalled": "{0} instalado",
"LabelNumberReviews": "{0} Cr\u00edticas",
"LabelNumberReviews": "{0} Avalia\u00e7\u00f5es",
"LabelFree": "Gr\u00e1tis",
"HeaderPlaybackError": "Erro na Reprodu\u00e7\u00e3o",
"MessagePlaybackErrorNotAllowed": "Voc\u00ea n\u00e3o est\u00e1 autorizado a reproduzir este conte\u00fado. Por favor, entre em contato com o administrador do sistema para mais detalhes.",
@ -537,7 +537,7 @@
"MessageFeatureIncludedWithSupporter": "Voc\u00ea est\u00e1 registrado para este recurso e poder\u00e1 continuar usando-o com uma ades\u00e3o ativa de colaborador.",
"MessageChangeRecurringPlanConfirm": "Depois de completar esta transa\u00e7\u00e3o voc\u00ea precisar\u00e1 cancelar sua doa\u00e7\u00e3o recorrente anterior dentro da conta do PayPal. Obrigado por colaborar com o Emby.",
"MessageSupporterMembershipExpiredOn": "Sua ades\u00e3o de colaborador expirou em {0}.",
"MessageYouHaveALifetimeMembership": "Voc\u00ea possui uma ades\u00e3o de colaborador vital\u00edcia. Voc\u00ea pode fazer doa\u00e7\u00f5es adicionais individuais ou de forma recorrente, usando as op\u00e7\u00f5es abaixo. Obrigado por colaborar com o Embu.",
"MessageYouHaveALifetimeMembership": "Voc\u00ea possui uma ades\u00e3o de colaborador vital\u00edcia. Voc\u00ea pode fazer doa\u00e7\u00f5es adicionais individuais ou de forma recorrente, usando as op\u00e7\u00f5es abaixo. Obrigado por colaborar com o Emby.",
"MessageYouHaveAnActiveRecurringMembership": "Voc\u00ea tem uma ades\u00e3o {0} ativa. Voc\u00ea pode atualizar seu plano usando as op\u00e7\u00f5es abaixo.",
"ButtonDelete": "Excluir",
"HeaderEmbyAccountAdded": "Conta do Emby Adicionada",

View file

@ -30,12 +30,12 @@
"NoPluginConfigurationMessage": "\u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u043f\u043b\u0430\u0433\u0438\u043d\u0435 \u043d\u0435\u0442 \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a.",
"NoPluginsInstalledMessage": "\u041d\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u043b\u0430\u0433\u0438\u043d\u0430.",
"BrowsePluginCatalogMessage": "\u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0438\u043c\u0435\u044e\u0449\u0438\u043c\u0438\u0441\u044f \u043f\u043b\u0430\u0433\u0438\u043d\u0430\u043c\u0438.",
"MessageKeyEmailedTo": "\u041a\u043b\u044e\u0447 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d \u042d-\u043f\u043e\u0447\u0442\u043e\u0439 \u043a {0}.",
"MessageKeyEmailedTo": "\u041a\u043b\u044e\u0447 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d \u043d\u0430 {0}.",
"MessageKeysLinked": "\u041a\u043b\u044e\u0447\u0438 \u0441\u0432\u044f\u0437\u0430\u043d\u044b.",
"HeaderConfirmation": "\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435",
"MessageKeyUpdated": "\u041a\u043b\u044e\u0447 \u0441\u043f\u043e\u043d\u0441\u043e\u0440\u0430 \u0431\u044b\u043b \u043e\u0431\u043d\u043e\u0432\u043b\u0451\u043d.",
"MessageKeyRemoved": "\u041a\u043b\u044e\u0447 \u0441\u043f\u043e\u043d\u0441\u043e\u0440\u0430 \u0431\u044b\u043b \u0443\u0434\u0430\u043b\u0451\u043d.",
"HeaderSupportTheTeam": "\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0442\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 Emby",
"HeaderSupportTheTeam": "\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0439\u0442\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 Emby",
"TextEnjoyBonusFeatures": "\u0412\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435\u0441\u044c \u0431\u043e\u043d\u0443\u0441\u043d\u044b\u043c\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438",
"TitleLiveTV": "\u0422\u0412-\u044d\u0444\u0438\u0440",
"TitleSync": "\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f",
@ -414,7 +414,7 @@
"ButtonNew": "\u0421\u043e\u0437\u0434\u0430\u0442\u044c",
"MessageInternetExplorerWebm": "\u0414\u043b\u044f \u043b\u0443\u0447\u0448\u0435\u0433\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 \u0432 Internet Explorer, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u043f\u043b\u0430\u0433\u0438\u043d \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u044f WebM.",
"HeaderVideoError": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u0438\u0434\u0435\u043e",
"ButtonAddToPlaylist": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a \u0441\u043f\u0438\u0441\u043a\u0443 \u0432\u043e\u0441\u043f\u0440-\u0438\u044f",
"ButtonAddToPlaylist": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u043f\u043b\u0435\u0439\u043b\u0438\u0441\u0442",
"HeaderAddToPlaylist": "\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a \u0441\u043f\u0438\u0441\u043a\u0443 \u0432\u043e\u0441\u043f\u0440-\u0438\u044f",
"LabelName": "\u0418\u043c\u044f (\u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435):",
"ButtonSubmit": "\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c",
@ -433,8 +433,8 @@
"HeaderRuntime": "\u0414\u043b\u0438\u0442-c\u0442\u044c",
"HeaderCommunityRating": "\u041e\u0431\u0449-\u0430\u044f \u043e\u0446\u0435\u043d\u043a\u0430",
"HeaderParentalRating": "\u0412\u043e\u0437\u0440-\u0430\u044f \u043a\u0430\u0442-\u0438\u044f",
"HeaderReleaseDate": "\u0414\u0430\u0442\u0430 \u0432\u044b\u043f\u0443\u0441\u043a\u0430",
"HeaderDateAdded": "\u0414\u0430\u0442\u0430 \u0434\u043e\u0431-\u0438\u044f",
"HeaderReleaseDate": "\u0414\u0430\u0442\u0430 \u0432\u044b\u0445\u043e\u0434\u0430",
"HeaderDateAdded": "\u0414\u0430\u0442\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f",
"HeaderSeries": "\u0421\u0435\u0440\u0438\u0430\u043b\u044b",
"HeaderSeason": "\u0421\u0435\u0437\u043e\u043d",
"HeaderSeasonNumber": "\u041d\u043e\u043c\u0435\u0440 \u0441\u0435\u0437\u043e\u043d\u0430",
@ -442,14 +442,14 @@
"HeaderYear": "\u0413\u043e\u0434",
"HeaderGameSystem": "\u0418\u0433\u0440\u043e\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430",
"HeaderPlayers": "\u041f\u0440\u043e\u0438\u0433\u0440\u044b\u0432\u0430\u0442\u0435\u043b\u0438",
"HeaderEmbeddedImage": "\u0412\u043d\u0435\u0434\u0440\u0451\u043d\u043d\u044b\u0439 \u0440\u0438\u0441\u0443\u043d\u043e\u043a",
"HeaderEmbeddedImage": "\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435",
"HeaderTrack": "\u0414\u043e\u0440\u043e\u0436\u043a\u0430",
"HeaderDisc": "\u0414\u0438\u0441\u043a",
"OptionMovies": "\u0424\u0438\u043b\u044c\u043c\u044b",
"OptionCollections": "\u041a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438",
"OptionSeries": "\u0422\u0412-\u0441\u0435\u0440\u0438\u0430\u043b\u044b",
"OptionSeasons": "\u0422\u0412-\u0441\u0435\u0437\u043e\u043d\u044b",
"OptionEpisodes": "\u0422\u0412-\u044d\u043f\u0438\u0437\u043e\u0434\u044b",
"OptionEpisodes": "\u042d\u043f\u0438\u0437\u043e\u0434\u044b",
"OptionGames": "\u0418\u0433\u0440\u044b",
"OptionGameSystems": "\u0418\u0433\u0440\u043e\u0432\u044b\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b",
"OptionMusicArtists": "\u041c\u0443\u0437\u044b\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0438\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u0438",
@ -515,13 +515,13 @@
"FolderTypeBooks": "\u041a\u043d\u0438\u0433\u0438",
"FolderTypeTvShows": "\u0422\u0412",
"TabMovies": "\u0424\u0438\u043b\u044c\u043c\u044b",
"TabSeries": "\u0421\u0435\u0440\u0438\u0430\u043b\u044b",
"TabSeries": "\u0422\u0412-\u0441\u0435\u0440\u0438\u0430\u043b\u044b",
"TabEpisodes": "\u042d\u043f\u0438\u0437\u043e\u0434\u044b",
"TabTrailers": "\u0422\u0440\u0435\u0439\u043b\u0435\u0440\u044b",
"TabGames": "\u0418\u0433\u0440\u044b",
"TabAlbums": "\u0410\u043b\u044c\u0431\u043e\u043c\u044b",
"TabSongs": "\u041c\u0435\u043b\u043e\u0434\u0438\u0438",
"TabMusicVideos": "\u041c\u0443\u0437\u044b\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0432\u0438\u0434\u0435\u043e",
"TabMusicVideos": "\u041c\u0443\u0437\u044b\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0432\u0438\u0434\u0435\u043e\u043a\u043b\u0438\u043f\u044b",
"BirthPlaceValue": "\u041c\u0435\u0441\u0442\u043e \u0440\u043e\u0436\u0434\u0435\u043d\u0438\u044f: {0}",
"DeathDateValue": "\u041a\u043e\u043d\u0447\u0438\u043d\u0430: {0}",
"BirthDateValue": "\u0420\u043e\u0436\u0434\u0435\u043d\u0438\u0435: {0}",
@ -544,7 +544,7 @@
"MessageEmbyAccountAdded": "\u0423\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c Emby \u0431\u044b\u043b\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f",
"MessagePendingEmbyAccountAdded": "\u0423\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c Emby \u0431\u044b\u043b\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u042d-\u043f\u043e\u0447\u0442\u0430 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0430 \u0432\u043b\u0430\u0434\u0435\u043b\u044c\u0446\u0443 \u0443\u0447\u0451\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438. \u041f\u0440\u0438\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u0435 \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c, \u0449\u0451\u043b\u043a\u043d\u0443\u0432 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 \u0432 \u042d-\u043f\u043e\u0447\u0442\u0435.",
"HeaderEmbyAccountRemoved": "\u0423\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c Emby \u0438\u0437\u044a\u044f\u0442\u0430",
"MessageEmbyAccontRemoved": "\u0423\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c Emby \u0431\u044b\u043b\u0430 \u0438\u0437\u044a\u044f\u0442\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f",
"MessageEmbyAccontRemoved": "\u0423\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c Emby \u0431\u044b\u043b\u0430 \u0438\u0437\u044a\u044f\u0442\u0430 \u0443 \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f",
"TooltipLinkedToEmbyConnect": "\u0418\u043c\u0435\u0435\u0442\u0441\u044f \u0441\u0432\u044f\u0437\u044c \u0441 Emby Connect",
"HeaderUnrated": "\u0411\u0435\u0437 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438",
"ValueDiscNumber": "\u0414\u0438\u0441\u043a {0}",
@ -557,7 +557,7 @@
"ButtonImDone": "\u042f \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u043b",
"OptionWatched": "\u041f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043e",
"OptionUnwatched": "\u041d\u0435 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043e",
"ExternalPlayerPlaystateOptionsHelp": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435, \u043a\u0430\u043a \u0432\u044b \u0445\u043e\u0442\u0435\u043b\u0438 \u0431\u044b \u0432\u043e\u0437\u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0432\u0438\u0434\u0435\u043e \u0432\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0440\u0430\u0437.",
"ExternalPlayerPlaystateOptionsHelp": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435, \u043a\u0430\u043a \u0432\u044b \u0445\u043e\u0442\u0435\u043b\u0438 \u0431\u044b \u0432\u043e\u0437\u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0432\u0438\u0434\u0435\u043e \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0440\u0430\u0437.",
"LabelMarkAs": "\u041e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u043a\u0430\u043a:",
"OptionInProgress": "\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f",
"LabelResumePoint": "\u0422\u043e\u0447\u043a\u0430 \u0432\u043e\u0437\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f:",
@ -585,7 +585,7 @@
"TooltipLike": "\u041d\u0440\u0430\u0432\u0438\u0442\u0441\u044f",
"TooltipDislike": "\u041d\u0435 \u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f",
"TooltipPlayed": "\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u043e",
"ValueSeriesYearToPresent": "{0} - \u043d\u044b\u043d\u0435",
"ValueSeriesYearToPresent": "{0} - \u0441\u0435\u0439\u0447\u0430\u0441",
"ValueAwards": "\u041f\u0440\u0438\u0437\u044b: {0}",
"ValueBudget": "\u0411\u044e\u0434\u0436\u0435\u0442: {0}",
"ValueRevenue": "\u0412\u044b\u0440\u0443\u0447\u043a\u0430: {0}",
@ -609,10 +609,10 @@
"MediaInfoExposureTime": "\u0412\u044b\u0434\u0435\u0440\u0436\u043a\u0430",
"MediaInfoFocalLength": "\u0424\u043e\u043a\u0443\u0441. \u0440\u0430\u0441\u0441\u0442-\u0438\u0435",
"MediaInfoOrientation": "\u041e\u0440\u0438\u0435\u043d\u0442\u0430\u0446\u0438\u044f",
"MediaInfoIsoSpeedRating": "\u0421\u0432\u0435\u0442\u043e\u0447\u0443\u0432\u0441\u0442-\u0442\u044c ISO",
"MediaInfoIsoSpeedRating": "\u0421\u0432\u0435\u0442\u043e\u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c ISO",
"MediaInfoLatitude": "\u0428\u0438\u0440\u043e\u0442\u0430",
"MediaInfoLongitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430",
"MediaInfoShutterSpeed": "\u0421\u043a\u043e\u0440. \u0437\u0430\u0442\u0432\u043e\u0440\u0430",
"MediaInfoShutterSpeed": "\u0412\u044b\u0434\u0435\u0440\u0436\u043a\u0430",
"MediaInfoSoftware": "\u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430",
"HeaderIfYouLikeCheckTheseOut": "\u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f \u00ab{0}\u00bb, \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441...",
"HeaderPlotKeywords": "\u041a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u0441\u043b\u043e\u0432\u0430 \u0441\u044e\u0436\u0435\u0442\u0430",
@ -633,12 +633,12 @@
"MediaInfoFormat": "\u0424\u043e\u0440\u043c\u0430\u0442",
"MediaInfoContainer": "\u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440",
"MediaInfoDefault": "\u0423\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u0435",
"MediaInfoForced": "\u0424\u043e\u0440\u0441-\u044b\u0435",
"MediaInfoForced": "\u0424\u043e\u0440\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435",
"MediaInfoExternal": "\u0412\u043d\u0435\u0448\u043d\u0438\u0435",
"MediaInfoTimestamp": "\u041c\u0435\u0442\u043a\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438",
"MediaInfoPixelFormat": "\u041f\u0438\u043a\u0441. \u0444-\u0442",
"MediaInfoBitDepth": "\u0420\u0430\u0437\u0440\u044f\u0434\u043d\u043e\u0441\u0442\u044c",
"MediaInfoSampleRate": "\u0427-\u0442\u0430 \u0434\u0438\u0441\u043a\u0440.",
"MediaInfoPixelFormat": "\u041f\u0438\u043a\u0441. \u0444\u043e\u0440\u043c\u0430\u0442",
"MediaInfoBitDepth": "\u0413\u043b\u0443\u0431\u0438\u043d\u0430 \u0446\u0432\u0435\u0442\u0430",
"MediaInfoSampleRate": "\u0427\u0430\u0441\u0442\u043e\u0442\u0430 \u0434\u0438\u0441\u043a\u0440\u0435\u0442\u0438\u0437\u0430\u0446\u0438\u0438",
"MediaInfoBitrate": "\u041f\u043e\u0442\u043e\u043a. \u0441\u043a-\u0442\u044c",
"MediaInfoChannels": "\u041a\u0430\u043d\u0430\u043b\u044b",
"MediaInfoLayout": "\u041a\u043e\u043c\u043f\u043e\u043d\u043e\u0432\u043a\u0430",
@ -648,8 +648,8 @@
"MediaInfoLevel": "\u0423\u0440\u043e\u0432\u0435\u043d\u044c",
"MediaInfoAspectRatio": "\u0421\u043e\u043e\u0442-\u0438\u0435 \u0441\u0442\u043e\u0440\u043e\u043d",
"MediaInfoResolution": "\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435",
"MediaInfoAnamorphic": "\u0410\u043d\u0430\u043c\u043e\u0440\u0444\u043d\u043e\u0435",
"MediaInfoInterlaced": "\u0427\u0435\u0440\u0435\u0441\u0441\u0442\u0440\u043e\u0447\u043d\u043e\u0435",
"MediaInfoAnamorphic": "\u0410\u043d\u0430\u043c\u043e\u0440\u0444\u043d\u043e\u0441\u0442\u044c",
"MediaInfoInterlaced": "\u0427\u0435\u0440\u0435\u0441\u0441\u0442\u0440\u043e\u0447\u043d\u043e\u0441\u0442\u044c",
"MediaInfoFramerate": "\u0427-\u0442\u0430 \u043a\u0430\u0434\u0440\u043e\u0432",
"MediaInfoStreamTypeAudio": "\u0410\u0443\u0434\u0438\u043e",
"MediaInfoStreamTypeData": "\u0414\u0430\u043d\u043d\u044b\u0435",
@ -660,7 +660,7 @@
"TabPlayback": "\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435",
"TabNotifications": "\u0423\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f",
"TabExpert": "\u0414\u043b\u044f \u043e\u043f\u044b\u0442\u043d\u044b\u0445",
"HeaderSelectCustomIntrosPath": "\u0412\u044b\u0431\u043e\u0440 \u043f\u0443\u0442\u0438 \u043a \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u043c \u0437\u0430\u0441\u0442\u0430\u0432\u043a\u0430\u043c",
"HeaderSelectCustomIntrosPath": "\u0412\u044b\u0431\u043e\u0440 \u043f\u0443\u0442\u0438 \u043a \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u043c \u0437\u0430\u0441\u0442\u0430\u0432\u043a\u0430\u043c",
"HeaderRateAndReview": "\u041e\u0446\u0435\u043d\u043a\u0430 \u0438 \u043e\u0442\u0437\u044b\u0432",
"HeaderThankYou": "\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u0438\u043c \u0432\u0430\u0441",
"MessageThankYouForYourReview": "\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u0438\u043c \u0437\u0430 \u0432\u0430\u0448 \u043e\u0442\u0437\u044b\u0432.",
@ -668,30 +668,30 @@
"LabelFullReview": "\u041e\u0442\u0437\u044b\u0432 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e:",
"LabelShortRatingDescription": "\u041a\u0440\u0430\u0442\u043a\u0430\u044f \u0441\u0432\u043e\u0434\u043a\u0430 \u043e\u0446\u0435\u043d\u043a\u0438:",
"OptionIRecommendThisItem": "\u042f \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u044d\u0442\u043e\u0442 \u044d\u043b\u0435\u043c\u0435\u043d\u0442",
"WebClientTourContent": "\u0421\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0441\u0432\u043e\u0438 \u043d\u0435\u0434\u0430\u0432\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043c\u0435\u0434\u0438\u0430\u0434\u0430\u043d\u043d\u044b\u0435, \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u044d\u043f\u0438\u0437\u043e\u0434\u044b \u0438 \u0442.\u0434. \u0417\u0435\u043b\u0451\u043d\u044b\u0435 \u043a\u0440\u0443\u0436\u043e\u0447\u043a\u0438 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442, \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0443 \u0432\u0430\u0441 \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u043d\u0435\u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0451\u043d\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432.",
"WebClientTourMovies": "\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435 \u0444\u0438\u043b\u044c\u043c\u044b, \u0442\u0440\u0435\u0439\u043b\u0435\u0440\u044b \u0438 \u0442.\u043f., \u0441 \u043b\u044e\u0431\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438\u043c\u0435\u044e\u0449\u0435\u0433\u043e \u0432\u0435\u0431-\u0431\u0440\u0430\u0443\u0437\u0435\u0440.",
"WebClientTourContent": "\u0421\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043d\u0435\u0434\u0430\u0432\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043c\u0435\u0434\u0438\u0430\u0434\u0430\u043d\u043d\u044b\u0435, \u043e\u0447\u0435\u0440\u0435\u0434\u043d\u044b\u0435 \u044d\u043f\u0438\u0437\u043e\u0434\u044b \u0438 \u0442.\u0434. \u0417\u0435\u043b\u0451\u043d\u044b\u0435 \u043a\u0440\u0443\u0436\u043e\u0447\u043a\u0438 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442, \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0443 \u0432\u0430\u0441 \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u043d\u0435\u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0451\u043d\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432.",
"WebClientTourMovies": "\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0438\u0442\u0435 \u0444\u0438\u043b\u044c\u043c\u044b, \u0442\u0440\u0435\u0439\u043b\u0435\u0440\u044b \u0438 \u0442.\u0434., \u0441 \u043b\u044e\u0431\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0441 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043e\u043c.",
"WebClientTourMouseOver": "\u0417\u0430\u0434\u0435\u0440\u0436\u0438\u0442\u0435 \u043a\u0443\u0440\u0441\u043e\u0440 \u043c\u044b\u0448\u0438 \u043d\u0430\u0434 \u043b\u044e\u0431\u044b\u043c \u043f\u043e\u0441\u0442\u0435\u0440\u043e\u043c \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0432\u0430\u0436\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438",
"WebClientTourTapHold": "\u041a\u043e\u0441\u043d\u0438\u0442\u0435\u0441\u044c \u0438 \u0443\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0439\u0442\u0435 \u0438\u043b\u0438 \u0449\u0435\u043b\u043a\u043d\u0438\u0442\u0435 \u043f\u0440\u0430\u0432\u043e\u0439 \u043a\u043d\u043e\u043f\u043a\u043e\u0439 \u043c\u044b\u0448\u0438 \u043f\u043e \u043b\u044e\u0431\u043e\u043c\u0443 \u043f\u043e\u0441\u0442\u0435\u0440\u0443 \u0434\u043b\u044f \u0432\u044b\u0437\u043e\u0432\u0430 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u0433\u043e \u043c\u0435\u043d\u044e",
"WebClientTourMetadataManager": "\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 \u041f\u0440\u0430\u0432\u0438\u0442\u044c, \u0447\u0442\u043e\u0431\u044b \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u043e\u043a\u043d\u043e \u0414\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440\u0430 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445",
"WebClientTourPlaylists": "\u0421\u0432\u043e\u0431\u043e\u0434\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043f\u0438\u0441\u043a\u0438 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u0438 \u0430\u0432\u0442\u043e\u043c\u0438\u043a\u0441\u044b, \u0438 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435 \u0438\u0445 \u043d\u0430 \u043b\u044e\u0431\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435",
"WebClientTourCollections": "\u0421\u043e\u0437\u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438 \u0444\u0438\u043b\u044c\u043c\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u0441\u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u0432\u043c\u0435\u0441\u0442\u0435",
"WebClientTourUserPreferences1": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0441\u043f\u043e\u0441\u043e\u0431, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0432\u0430\u0448\u0430 \u043c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0430 \u0432\u043e \u0432\u0441\u0435\u0445 \u0432\u0430\u0448\u0438\u0445 Emby-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445.",
"WebClientTourTapHold": "\u041a\u043e\u0441\u043d\u0438\u0442\u0435\u0441\u044c \u0438 \u0443\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0439\u0442\u0435 \u0438\u043b\u0438 \u0449\u0451\u043b\u043a\u043d\u0438\u0442\u0435 \u043f\u0440\u0430\u0432\u043e\u0439 \u043a\u043d\u043e\u043f\u043a\u043e\u0439 \u043c\u044b\u0448\u0438 \u043b\u044e\u0431\u043e\u0439 \u043f\u043e\u0441\u0442\u0435\u0440 \u0434\u043b\u044f \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u0433\u043e \u043c\u0435\u043d\u044e",
"WebClientTourMetadataManager": "\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 \u041f\u0440\u0430\u0432\u0438\u0442\u044c, \u0447\u0442\u043e\u0431\u044b \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0414\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445",
"WebClientTourPlaylists": "\u0411\u0435\u0437 \u0443\u0441\u0438\u043b\u0438\u0439 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043f\u0438\u0441\u043a\u0438 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u0438 \u0430\u0432\u0442\u043e\u043c\u0438\u043a\u0441\u044b, \u0438 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435 \u0438\u0445 \u043d\u0430 \u043b\u044e\u0431\u043e\u043c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435",
"WebClientTourCollections": "\u0421\u043e\u0437\u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0444\u0438\u043b\u044c\u043c\u043e\u0432\u044b\u0435 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438, \u0447\u0442\u043e\u0431\u044b \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0442\u044b \u0432\u043c\u0435\u0441\u0442\u0435",
"WebClientTourUserPreferences1": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0441\u043f\u043e\u0441\u043e\u0431, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0430 \u0432\u043e \u0432\u0441\u0435\u0445 \u0432\u0430\u0448\u0438\u0445 Emby-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445.",
"WebClientTourUserPreferences2": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0430\u0443\u0434\u0438\u043e \u0438 \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u043e\u0432 \u0441\u0432\u043e\u0435\u0433\u043e \u044f\u0437\u044b\u043a\u0430 \u0435\u0434\u0438\u043d\u043e\u0436\u0434\u044b, \u0434\u043b\u044f \u0432\u0441\u044f\u043a\u043e\u0433\u043e Emby-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f",
"WebClientTourUserPreferences3": "\u041e\u0444\u043e\u0440\u044c\u043c\u0442\u0435 \u0433\u043b\u0430\u0432\u043d\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0432\u0435\u0431-\u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043f\u043e \u0441\u0432\u043e\u0438\u043c \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0435\u043d\u0438\u044f\u043c",
"WebClientTourUserPreferences4": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 \u0437\u0430\u0434\u043d\u0438\u043a\u0438, \u0442\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043c\u0435\u043b\u043e\u0434\u0438\u0438 \u0438 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u043f\u0440\u043e\u0438\u0433\u0440\u044b\u0432\u0430\u0442\u0435\u043b\u0438",
"WebClientTourUserPreferences4": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u0437\u0430\u0434\u043d\u0438\u043a\u0438, \u0442\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043c\u0435\u043b\u043e\u0434\u0438\u0438 \u0438 \u0432\u043d\u0435\u0448\u043d\u0438\u0435 \u043f\u0440\u043e\u0438\u0433\u0440\u044b\u0432\u0430\u0442\u0435\u043b\u0438",
"WebClientTourMobile1": "\u0412\u0435\u0431-\u043a\u043b\u0438\u0435\u043d\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043e\u0442\u043b\u0438\u0447\u043d\u043e \u043d\u0430 \u0441\u043c\u0430\u0440\u0442\u0444\u043e\u043d\u0430\u0445 \u0438 \u043f\u043b\u0430\u043d\u0448\u0435\u0442\u0430\u0445...",
"WebClientTourMobile2": "\u0438 \u0441 \u043b\u0451\u0433\u043a\u043e\u0441\u0442\u044c\u044e \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0439\u0442\u0435 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 \u0438 Emby-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c\u0438",
"WebClientTourMobile2": "\u0438 \u043b\u0435\u0433\u043a\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 \u0438 Emby-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c\u0438",
"WebClientTourMySync": "\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u0443\u0439\u0442\u0435 \u0441\u0432\u043e\u0438 \u043b\u0438\u0447\u043d\u044b\u0435 \u043c\u0435\u0434\u0438\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u0432\u0430\u0448\u0438\u043c\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430.",
"MessageEnjoyYourStay": "\u041f\u0440\u0438\u044f\u0442\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u044f\u043f\u0440\u043e\u0432\u043e\u0436\u0434\u0435\u043d\u0438\u044f",
"DashboardTourDashboard": "\u0421\u0435\u0440\u0432\u0435\u0440\u043d\u0430\u044f \u0418\u043d\u0444\u043e\u043f\u0430\u043d\u0435\u043b\u044c \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u0412\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u0432\u0441\u0435\u0433\u0434\u0430 \u0437\u043d\u0430\u0442\u044c, \u043a\u0442\u043e \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f \u0447\u0435\u043c \u0438 \u0433\u0434\u0435 \u043e\u043d\u0438 \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f.",
"MessageEnjoyYourStay": "\u041f\u0440\u0438\u044f\u0442\u043d\u043e\u0433\u043e \u043f\u0440\u0435\u0431\u044b\u0432\u0430\u043d\u0438\u044f",
"DashboardTourDashboard": "\u0421\u0435\u0440\u0432\u0435\u0440\u043d\u0430\u044f \u0418\u043d\u0444\u043e\u043f\u0430\u043d\u0435\u043b\u044c \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u0441\u043b\u0435\u0436\u0435\u043d\u0438\u044f \u0437\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u0438 \u0432\u0430\u0448\u0438\u043c\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438. \u0412\u044b \u0432\u0441\u0435\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442\u0435 \u0437\u043d\u0430\u0442\u044c, \u043a\u0442\u043e \u0447\u0435\u043c \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f, \u0438 \u043a\u0442\u043e \u0433\u0434\u0435 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f.",
"DashboardTourHelp": "\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u0441\u043f\u0440\u0430\u0432\u043a\u0430 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u043a\u043d\u043e\u043f\u043a\u0438, \u0447\u0442\u043e\u0431\u044b \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0432\u0438\u043a\u0438-\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u043e\u0442\u043d\u043e\u0441\u044f\u0449\u0438\u0445\u0441\u044f \u043a \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044e \u044d\u043a\u0440\u0430\u043d\u0430.",
"DashboardTourUsers": "\u0421\u0432\u043e\u0431\u043e\u0434\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0443\u0447\u0451\u0442\u043d\u044b\u0435 \u0437\u0430\u043f\u0438\u0441\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0434\u043b\u044f \u0432\u0430\u0448\u0438\u0445 \u0434\u0440\u0443\u0437\u0435\u0439 \u0438 \u0447\u043b\u0435\u043d\u043e\u0432 \u0441\u0435\u043c\u044c\u0438, \u043a\u0430\u0436\u0434\u0443\u044e \u0441 \u0438\u0445 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c\u0438 \u043f\u0440\u0430\u0432\u0430\u043c\u0438, \u0434\u043e\u0441\u0442\u0443\u043f\u043e\u043c \u043a \u043c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0435, \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435\u043c \u0438 \u0442.\u0434.",
"DashboardTourCinemaMode": "\u0420\u0435\u0436\u0438\u043c \u043a\u0438\u043d\u043e\u0442\u0435\u0430\u0442\u0440\u0430 \u043f\u0440\u0438\u0432\u043d\u043e\u0441\u0438\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043a\u0438\u043d\u043e\u0437\u0430\u043b\u0430 \u043f\u0440\u044f\u043c\u0438\u043a\u043e\u043c \u0432\u043e \u0432\u0430\u0448\u0443 \u0433\u043e\u0441\u0442\u0438\u043d\u0443\u044e, \u0432\u043c\u0435\u0441\u0442\u0435 \u0441\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c\u044e \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c \u0442\u0440\u0435\u0439\u043b\u0435\u0440\u044b \u0438 \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u0437\u0430\u0441\u0442\u0430\u0432\u043a\u0438 \u043f\u0435\u0440\u0435\u0434 \u0433\u043b\u0430\u0432\u043d\u044b\u043c \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u043e\u043c.",
"DashboardTourUsers": "\u0411\u0435\u0437 \u0443\u0441\u0438\u043b\u0438\u0439 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0443\u0447\u0451\u0442\u043d\u044b\u0435 \u0437\u0430\u043f\u0438\u0441\u0438 \u0432\u0430\u0448\u0438\u0445 \u0434\u0440\u0443\u0437\u0435\u0439 \u0438 \u0447\u043b\u0435\u043d\u043e\u0432 \u0441\u0435\u043c\u044c\u0438, \u043a\u0430\u0436\u0434\u0443\u044e \u0441 \u0438\u0445 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c\u0438 \u043f\u0440\u0430\u0432\u0430\u043c\u0438, \u0434\u043e\u0441\u0442\u0443\u043f\u043e\u043c \u043a \u043c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0435, \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435\u043c \u0438 \u0442.\u0434.",
"DashboardTourCinemaMode": "\u0420\u0435\u0436\u0438\u043c \u043a\u0438\u043d\u043e\u0442\u0435\u0430\u0442\u0440\u0430 \u043f\u0440\u0438\u0432\u043d\u043e\u0441\u0438\u0442 \u043e\u0449\u0443\u0449\u0435\u043d\u0438\u0435 \u043a\u0438\u043d\u043e\u0437\u0430\u043b\u0430 \u043f\u0440\u044f\u043c\u0438\u043a\u043e\u043c \u0432\u043e \u0432\u0430\u0448\u0443 \u0433\u043e\u0441\u0442\u0438\u043d\u0443\u044e, \u0432\u043c\u0435\u0441\u0442\u0435 \u0441\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c\u044e \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c \u0442\u0440\u0435\u0439\u043b\u0435\u0440\u044b \u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0435 \u0437\u0430\u0441\u0442\u0430\u0432\u043a\u0438 \u043f\u0435\u0440\u0435\u0434 \u0433\u043b\u0430\u0432\u043d\u044b\u043c \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u043e\u043c.",
"DashboardTourChapters": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0435 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0440\u0438\u0441\u0443\u043d\u043a\u043e\u0432 \u0441\u0446\u0435\u043d \u043a \u0432\u0438\u0434\u0435\u043e, \u0434\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u0438\u0432\u043b\u0435\u043a\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430.",
"DashboardTourSubtitles": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0439\u0442\u0435 \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u044b \u0434\u043b\u044f \u0432\u0438\u0434\u0435\u043e \u043d\u0430 \u043b\u044e\u0431\u043e\u043c \u044f\u0437\u044b\u043a\u0435.",
"DashboardTourPlugins": "\u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0439\u0442\u0435 \u043f\u043b\u0430\u0433\u0438\u043d\u044b, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u043a\u0430\u043d\u0430\u043b\u043e\u0432 \u0432\u0438\u0434\u0435\u043e, \u0422\u0412-\u044d\u0444\u0438\u0440\u0430, \u0441\u043a\u0430\u043d\u043d\u0435\u0440\u043e\u0432 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0442.\u0434.",
"DashboardTourSubtitles": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u044b \u043d\u0430 \u043b\u044e\u0431\u043e\u043c \u044f\u0437\u044b\u043a\u0435 \u0434\u043b\u044f \u0432\u0430\u0448\u0438\u0445 \u0432\u0438\u0434\u0435\u043e.",
"DashboardTourPlugins": "\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u043f\u043b\u0430\u0433\u0438\u043d\u044b, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u043a\u0430\u043d\u0430\u043b\u043e\u0432 \u0432\u0438\u0434\u0435\u043e, \u0422\u0412-\u044d\u0444\u0438\u0440\u0430, \u0441\u043a\u0430\u043d\u043d\u0435\u0440\u043e\u0432 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0442.\u0434.",
"DashboardTourNotifications": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043e \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u0445 \u043d\u0430 \u0432\u0430\u0448\u0435 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430, \u044d-\u043f\u043e\u0447\u0442\u0443 \u0438 \u0442.\u0434.",
"DashboardTourScheduledTasks": "\u0421\u0432\u043e\u0431\u043e\u0434\u043d\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0439\u0442\u0435 \u0434\u043e\u043b\u0433\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c\u0438 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447. \u041f\u0440\u0438\u043d\u0438\u043c\u0430\u0439\u0442\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043a\u043e\u0433\u0434\u0430 \u043e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c\u0441\u044f, \u0438 \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0447\u0430\u0441\u0442\u043e.",
"DashboardTourScheduledTasks": "\u0411\u0435\u0437 \u0443\u0441\u0438\u043b\u0438\u0439 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0439\u0442\u0435 \u0434\u043e\u043b\u0433\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c\u0438 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447. \u041f\u0440\u0438\u043d\u0438\u043c\u0430\u0439\u0442\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u043a\u043e\u0433\u0434\u0430 \u043e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u044b, \u0438 \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0447\u0430\u0441\u0442\u043e.",
"DashboardTourMobile": "\u0418\u043d\u0444\u043e\u043f\u0430\u043d\u0435\u043b\u044c Emby Server \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0430 \u0441\u043c\u0430\u0440\u0442\u0444\u043e\u043d\u0430\u0445 \u0438 \u043f\u043b\u0430\u043d\u0448\u0435\u0442\u0430\u0445. \u0423\u043f\u0440\u0430\u0432\u043b\u044f\u0439\u0442\u0435 \u0441\u0432\u043e\u0438\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u0441 \u043b\u0430\u0434\u043e\u043d\u0438 \u0432 \u043b\u044e\u0431\u043e\u0435 \u0432\u0440\u0435\u043c\u044f, \u0441 \u043b\u044e\u0431\u043e\u0433\u043e \u043c\u0435\u0441\u0442\u0430.",
"DashboardTourSync": "\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u0443\u0439\u0442\u0435 \u0441\u0432\u043e\u0438 \u043b\u0438\u0447\u043d\u044b\u0435 \u043c\u0435\u0434\u0438\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u0432\u0430\u0448\u0438\u043c\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430.",
"MessageRefreshQueued": "\u041f\u043e\u0434\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u0438",

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