From 41a63028f1e3ecd54c676135ce01934be368691a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 15 May 2015 12:02:26 -0400 Subject: fixes #101 - FFMpeg iTunes m4a transcode issue --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 27 ++++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'MediaBrowser.Api/Playback/BaseStreamingService.cs') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 5e06ab1d0..d3241a443 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -769,26 +769,31 @@ namespace MediaBrowser.Api.Playback /// System.Nullable{System.Int32}. private int? GetNumAudioChannelsParam(StreamRequest request, MediaStream audioStream, string outputAudioCodec) { - if (audioStream != null) - { - var codec = outputAudioCodec ?? string.Empty; + var inputChannels = audioStream == null + ? null + : audioStream.Channels; - if (audioStream.Channels > 2 && codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1) - { - // wmav2 currently only supports two channel output - return 2; - } + var codec = outputAudioCodec ?? string.Empty; + + if (codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1) + { + // wmav2 currently only supports two channel output + return Math.Min(2, inputChannels ?? 2); } if (request.MaxAudioChannels.HasValue) { - if (audioStream != null && audioStream.Channels.HasValue) + if (inputChannels.HasValue) { - return Math.Min(request.MaxAudioChannels.Value, audioStream.Channels.Value); + return Math.Min(request.MaxAudioChannels.Value, inputChannels.Value); } + var channelLimit = codec.IndexOf("mp3", StringComparison.OrdinalIgnoreCase) != -1 + ? 2 + : 5; + // If we don't have any media info then limit it to 5 to prevent encoding errors due to asking for too many channels - return Math.Min(request.MaxAudioChannels.Value, 5); + return Math.Min(request.MaxAudioChannels.Value, channelLimit); } return request.AudioChannels; -- cgit v1.2.3 From 448dd949c78d2309a8dd27fc2c22cfa19594cadb Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 19 May 2015 15:15:40 -0400 Subject: update script loading --- MediaBrowser.Api/ApiEntryPoint.cs | 8 +-- MediaBrowser.Api/Playback/BaseStreamingService.cs | 55 ++++++++++++++++-- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 67 +++++++++++++++++++--- MediaBrowser.Api/Playback/TranscodingThrottler.cs | 2 +- .../Configuration/EncodingOptions.cs | 4 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 2 +- .../Localization/Server/server.json | 7 --- .../Sync/SyncManager.cs | 9 ++- MediaBrowser.WebDashboard/Api/PackageCreator.cs | 14 ----- .../MediaBrowser.WebDashboard.csproj | 4 +- 10 files changed, 126 insertions(+), 46 deletions(-) (limited to 'MediaBrowser.Api/Playback/BaseStreamingService.cs') diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 2cd900754..2a72854fb 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -250,19 +250,19 @@ namespace MediaBrowser.Api return GetTranscodingJob(path, type) != null; } - public TranscodingJob GetTranscodingJob(string path, TranscodingJobType type) + public TranscodingJob GetTranscodingJobByPlaySessionId(string playSessionId) { lock (_activeTranscodingJobs) { - return _activeTranscodingJobs.FirstOrDefault(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase)); + return _activeTranscodingJobs.FirstOrDefault(j => j.PlaySessionId.Equals(playSessionId, StringComparison.OrdinalIgnoreCase)); } } - public TranscodingJob GetTranscodingJob(string id) + public TranscodingJob GetTranscodingJob(string path, TranscodingJobType type) { lock (_activeTranscodingJobs) { - return _activeTranscodingJobs.FirstOrDefault(j => j.Id.Equals(id, StringComparison.OrdinalIgnoreCase)); + return _activeTranscodingJobs.FirstOrDefault(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase)); } } diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index d3241a443..a33a7df8d 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -148,6 +148,7 @@ namespace MediaBrowser.Api.Playback } protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); + private readonly long _slowSeekTicks = TimeSpan.FromSeconds(2).Ticks; /// /// Gets the fast seek command line parameter. @@ -157,16 +158,41 @@ namespace MediaBrowser.Api.Playback /// The fast seek command line parameter. protected string GetFastSeekCommandLineParameter(StreamRequest request) { - var time = request.StartTimeTicks; + var time = request.StartTimeTicks ?? 0; - if (time.HasValue && time.Value > 0) + if (time > 0) { - return string.Format("-ss {0}", MediaEncoder.GetTimeParameter(time.Value)); + if (time > _slowSeekTicks && EnableSlowSeek) + { + time -= _slowSeekTicks; + } + + return string.Format("-ss {0}", MediaEncoder.GetTimeParameter(time)); } return string.Empty; } + protected string GetSlowSeekCommandLineParameter(StreamRequest request) + { + var time = request.StartTimeTicks ?? 0; + + if (time > _slowSeekTicks) + { + return string.Format("-ss {0}", MediaEncoder.GetTimeParameter(_slowSeekTicks)); + } + + return string.Empty; + } + + protected virtual bool EnableSlowSeek + { + get + { + return false; + } + } + /// /// Gets the map args. /// @@ -1060,7 +1086,7 @@ namespace MediaBrowser.Api.Playback private void StartThrottler(StreamState state, TranscodingJob transcodingJob) { - if (state.InputProtocol == MediaProtocol.File && + if (EnableThrottling && state.InputProtocol == MediaProtocol.File && state.RunTimeTicks.HasValue && state.VideoType == VideoType.VideoFile && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) @@ -1073,6 +1099,14 @@ namespace MediaBrowser.Api.Playback } } + protected virtual bool EnableThrottling + { + get + { + return true; + } + } + private async void StartStreamingLog(TranscodingJob transcodingJob, StreamState state, Stream source, Stream target) { try @@ -1695,6 +1729,11 @@ namespace MediaBrowser.Api.Playback private void TryStreamCopy(StreamState state, VideoStreamRequest videoRequest) { + if (!EnableStreamCopy) + { + return; + } + if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream)) { state.OutputVideoCodec = "copy"; @@ -1706,6 +1745,14 @@ namespace MediaBrowser.Api.Playback } } + protected virtual bool EnableStreamCopy + { + get + { + return true; + } + } + private void AttachMediaSourceInfo(StreamState state, MediaSourceInfo mediaSource, VideoStreamRequest videoRequest, diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 1f6bc242d..f5345df72 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -62,7 +62,8 @@ namespace MediaBrowser.Api.Playback.Hls public class DynamicHlsService : BaseHlsService { - public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer) + public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, INetworkManager networkManager) + : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer) { NetworkManager = networkManager; } @@ -130,7 +131,7 @@ namespace MediaBrowser.Api.Playback.Hls { var startTranscoding = false; - var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension); + var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, request.PlaySessionId, segmentExtension); var segmentGapRequiringTranscodingChange = 24 / state.SegmentLength; if (currentTranscodingIndex == null) @@ -206,9 +207,11 @@ namespace MediaBrowser.Api.Playback.Hls return position; } - public int? GetCurrentTranscodingIndex(string playlist, string segmentExtension) + public int? GetCurrentTranscodingIndex(string playlist, string playSessionId, string segmentExtension) { - var job = ApiEntryPoint.Instance.GetTranscodingJob(playlist, TranscodingJobType); + var job = string.IsNullOrWhiteSpace(playSessionId) ? + ApiEntryPoint.Instance.GetTranscodingJob(playlist, TranscodingJobType) : + ApiEntryPoint.Instance.GetTranscodingJobByPlaySessionId(playSessionId); if (job == null || job.HasExited) { @@ -720,11 +723,31 @@ namespace MediaBrowser.Api.Playback.Hls // If isEncoding is true we're actually starting ffmpeg var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0"; + var toTimeParam = string.Empty; + if (state.RunTimeTicks.HasValue) + { + var startTime = state.Request.StartTimeTicks ?? 0; + var durationSeconds = ApiEntryPoint.Instance.GetEncodingOptions().ThrottleThresholdInSeconds; + var endTime = startTime + TimeSpan.FromSeconds(durationSeconds).Ticks; + endTime = Math.Min(endTime, state.RunTimeTicks.Value); + + if (endTime < state.RunTimeTicks.Value) + { + toTimeParam = " -to " + MediaEncoder.GetTimeParameter(endTime); + } + } + + var slowSeekParam = GetSlowSeekCommandLineParameter(state.Request); + if (!string.IsNullOrWhiteSpace(slowSeekParam)) + { + slowSeekParam = " " + slowSeekParam; + } + if (state.EnableGenericHlsSegmenter) { var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d.ts"; - return string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -flags -global_header -sc_threshold 0 {5} -f segment -segment_time {6} -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", + return string.Format("{0} {1}{10}{11} -map_metadata -1 -threads {2} {3} {4} -flags -global_header -sc_threshold 0 {5} -f segment -segment_time {6} -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", inputModifier, GetInputArgument(state), threads, @@ -734,11 +757,13 @@ namespace MediaBrowser.Api.Playback.Hls state.SegmentLength.ToString(UsCulture), startNumberParam, outputPath, - outputTsArg + outputTsArg, + slowSeekParam, + toTimeParam ).Trim(); } - return string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -flags -global_header -copyts -sc_threshold 0 {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", + return string.Format("{0} {1}{10}{11} -map_metadata -1 -threads {2} {3} {4} -flags -global_header -copyts -sc_threshold 0 {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", inputModifier, GetInputArgument(state), threads, @@ -748,10 +773,36 @@ namespace MediaBrowser.Api.Playback.Hls state.SegmentLength.ToString(UsCulture), startNumberParam, state.HlsListSize.ToString(UsCulture), - outputPath + outputPath, + slowSeekParam, + toTimeParam ).Trim(); } + protected override bool EnableThrottling + { + get + { + return false; + } + } + + protected override bool EnableStreamCopy + { + get + { + return false; + } + } + + protected override bool EnableSlowSeek + { + get + { + return true; + } + } + /// /// Gets the segment file extension. /// diff --git a/MediaBrowser.Api/Playback/TranscodingThrottler.cs b/MediaBrowser.Api/Playback/TranscodingThrottler.cs index ece455009..fec3dda86 100644 --- a/MediaBrowser.Api/Playback/TranscodingThrottler.cs +++ b/MediaBrowser.Api/Playback/TranscodingThrottler.cs @@ -42,7 +42,7 @@ namespace MediaBrowser.Api.Playback var options = GetOptions(); - if (options.EnableThrottling && IsThrottleAllowed(_job, options.ThrottleThresholdSeconds)) + if (options.EnableThrottling && IsThrottleAllowed(_job, options.ThrottleThresholdInSeconds)) { PauseTranscoding(); } diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 7f1607217..ae714a84e 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -9,7 +9,7 @@ namespace MediaBrowser.Model.Configuration public string H264Encoder { get; set; } public bool EnableDebugLogging { get; set; } public bool EnableThrottling { get; set; } - public int ThrottleThresholdSeconds { get; set; } + public int ThrottleThresholdInSeconds { get; set; } public EncodingOptions() { @@ -17,7 +17,7 @@ namespace MediaBrowser.Model.Configuration DownMixAudioBoost = 2; EncodingQuality = EncodingQuality.Auto; EnableThrottling = true; - ThrottleThresholdSeconds = 110; + ThrottleThresholdInSeconds = 120; } } } diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 8400b204f..3c1c7ea6d 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -485,7 +485,7 @@ namespace MediaBrowser.Model.Dlna if (targetAudioChannels.HasValue) { - if (targetAudioChannels.Value >= 5 && (maxTotalBitrate ?? 0) >= 1500000) + if (targetAudioChannels.Value >= 5 && (maxTotalBitrate ?? 0) >= 2000000) { defaultBitrate = 320000; } diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index f2999ea02..8f90f1aca 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -1424,13 +1424,6 @@ "OptionEnableFullSpeedConversionHelp": "By default, sync conversion is performed at a low speed to minimize resource consumption.", "HeaderPlaylists": "Playlists", "HeaderSelectDate": "Select Date", - "HeaderWelcomeExclamation": "Welcome!", - "HeaderMyPreferences": "My Preferences", - "ButtonMyPreferencesWelcomeYes": "Yes, I'd like to set my preferences now.", - "ButtonMyPreferencesWelcomeNo": "No thanks, I'll do it later.", - "MyPreferencesWelcomeMessage1": "We've presented your library in a way we think you'll enjoy. The appearance and grouping of content can be changed anytime by adjusting your preferences. Your preferences will apply to all Emby apps.", - "MyPreferencesWelcomeMessage2": "Would you like to set your preferences now?", - "ToAccessPreferencesHelp": "To access your preferences later, click your user icon in the top right header and select My Preferences.", "HeaderViewStyles": "View Styles", "LabelSelectViewStyles": "Enable enhanced presentations for:", "LabelSelectViewStylesHelp": "If enabled, views will be built with metadata to offer categories such as Suggestions, Latest, Genres, and more. If disabled, they'll be displayed with simple folders.", diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index 0e4a3bcf1..d47135c65 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs @@ -286,6 +286,13 @@ namespace MediaBrowser.Server.Implementations.Sync private async Task FillMetadata(SyncJob job) { + var user = _userManager.GetUserById(job.UserId); + + if (user == null) + { + return; + } + var target = GetSyncTargets(job.UserId) .FirstOrDefault(i => string.Equals(i.Id, job.TargetId, StringComparison.OrdinalIgnoreCase)); @@ -302,8 +309,6 @@ namespace MediaBrowser.Server.Implementations.Sync { var processor = GetSyncJobProcessor(); - var user = _userManager.GetUserById(job.UserId); - item = (await processor .GetItemsForSync(job.Category, job.ParentId, job.RequestedItemIds, user, job.UnwatchedOnly).ConfigureAwait(false)) .FirstOrDefault(); diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index bbdb733c1..6b8c17002 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -474,11 +474,6 @@ namespace MediaBrowser.WebDashboard.Api } apiClientFiles.Add("thirdparty/apiclient/connectionmanager.js"); - if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase)) - { - apiClientFiles.Add("thirdparty/cordova/remotecontrols.js"); - } - foreach (var file in apiClientFiles) { using (var fs = _fileSystem.GetFileStream(GetDashboardResourcePath(file), FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) @@ -561,13 +556,9 @@ namespace MediaBrowser.WebDashboard.Api "alphapicker.js", "addpluginpage.js", - "metadataadvanced.js", "autoorganizetv.js", "autoorganizelog.js", - "channelslatest.js", - "channelitems.js", "channelsettings.js", - "connectlogin.js", "dashboardgeneral.js", "dashboardpage.js", "devicesupload.js", @@ -585,16 +576,13 @@ namespace MediaBrowser.WebDashboard.Api "encodingsettings.js", "externalplayer.js", - "favorites.js", "forgotpassword.js", "forgotpasswordpin.js", - "homelatest.js", "indexpage.js", "itembynamedetailpage.js", "itemdetailpage.js", "kids.js", "librarypathmapping.js", - "reports.js", "librarysettings.js", "livetvchannel.js", "livetvguide.js", @@ -607,7 +595,6 @@ namespace MediaBrowser.WebDashboard.Api "livetvsettings.js", "livetvstatus.js", - "loginpage.js", "medialibrarypage.js", "metadataconfigurationpage.js", "metadataimagespage.js", @@ -632,7 +619,6 @@ namespace MediaBrowser.WebDashboard.Api "scheduledtaskpage.js", "scheduledtaskspage.js", "search.js", - "selectserver.js", "supporterkeypage.js", "syncactivity.js", "syncsettings.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 215fb5e5e..b7ecd58a5 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -222,15 +222,13 @@ PreserveNewest - - PreserveNewest - PreserveNewest PreserveNewest + PreserveNewest -- cgit v1.2.3 From 71b8f87cb70417d56d14c4912a0e5b94077d9c87 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 20 May 2015 12:28:55 -0400 Subject: hls updates --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 4 +- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 76 ++++++++++++++++++++-- MediaBrowser.WebDashboard/Api/PackageCreator.cs | 1 - 3 files changed, 73 insertions(+), 8 deletions(-) (limited to 'MediaBrowser.Api/Playback/BaseStreamingService.cs') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index a33a7df8d..6f1edb165 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -148,7 +148,7 @@ namespace MediaBrowser.Api.Playback } protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - private readonly long _slowSeekTicks = TimeSpan.FromSeconds(2).Ticks; + private readonly long _slowSeekTicks = TimeSpan.FromSeconds(0).Ticks; /// /// Gets the fast seek command line parameter. @@ -177,7 +177,7 @@ namespace MediaBrowser.Api.Playback { var time = request.StartTimeTicks ?? 0; - if (time > _slowSeekTicks) + if (time > _slowSeekTicks && _slowSeekTicks > 0) { return string.Format("-ss {0}", MediaEncoder.GetTimeParameter(_slowSeekTicks)); } diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index f5345df72..2d8b71a0c 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -13,6 +13,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using ServiceStack; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -156,12 +157,14 @@ namespace MediaBrowser.Api.Playback.Hls { ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.PlaySessionId, p => false); + await ReadSegmentLengths(playlistPath).ConfigureAwait(false); + if (currentTranscodingIndex.HasValue) { DeleteLastFile(playlistPath, segmentExtension, 0); } - request.StartTimeTicks = GetSeekPositionTicks(state, requestedIndex); + request.StartTimeTicks = GetSeekPositionTicks(state, playlistPath, requestedIndex); job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false); } @@ -199,11 +202,73 @@ namespace MediaBrowser.Api.Playback.Hls return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false); } - private long GetSeekPositionTicks(StreamState state, int requestedIndex) + private static readonly ConcurrentDictionary SegmentLengths = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private async Task ReadSegmentLengths(string playlist) { - var startSeconds = requestedIndex * state.SegmentLength; - var position = TimeSpan.FromSeconds(startSeconds).Ticks; + try + { + using (var fileStream = GetPlaylistFileStream(playlist)) + { + using (var reader = new StreamReader(fileStream)) + { + double duration = -1; + + while (!reader.EndOfStream) + { + var text = await reader.ReadLineAsync().ConfigureAwait(false); + + if (text.StartsWith("#EXTINF", StringComparison.OrdinalIgnoreCase)) + { + var parts = text.Split(new[] { ':' }, 2); + if (parts.Length == 2) + { + var time = parts[1].Trim(new[] { ',' }).Trim(); + double timeValue; + if (double.TryParse(time, NumberStyles.Any, CultureInfo.InvariantCulture, out timeValue)) + { + duration = timeValue; + continue; + } + } + } + else if (duration != -1) + { + SegmentLengths.AddOrUpdate(text, duration, (k, v) => duration); + Logger.Debug("Added segment length of {0} for {1}", duration, text); + } + + duration = -1; + } + } + } + } + catch (FileNotFoundException) + { + + } + } + private long GetSeekPositionTicks(StreamState state, string playlist, int requestedIndex) + { + double startSeconds = 0; + + for (var i = 0; i < requestedIndex; i++) + { + var segmentPath = GetSegmentPath(playlist, i); + + double length; + if (SegmentLengths.TryGetValue(Path.GetFileName(segmentPath), out length)) + { + Logger.Debug("Found segment length of {0} for index {1}", length, i); + startSeconds += length; + } + else + { + startSeconds += state.SegmentLength; + } + } + + var position = TimeSpan.FromSeconds(startSeconds).Ticks; return position; } @@ -693,7 +758,7 @@ namespace MediaBrowser.Api.Playback.Hls } var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", - state.SegmentLength.ToString(UsCulture)); + 1.ToString(UsCulture)); var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; @@ -728,6 +793,7 @@ namespace MediaBrowser.Api.Playback.Hls { var startTime = state.Request.StartTimeTicks ?? 0; var durationSeconds = ApiEntryPoint.Instance.GetEncodingOptions().ThrottleThresholdInSeconds; + var endTime = startTime + TimeSpan.FromSeconds(durationSeconds).Ticks; endTime = Math.Min(endTime, state.RunTimeTicks.Value); diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 6b8c17002..2823733ea 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -619,7 +619,6 @@ namespace MediaBrowser.WebDashboard.Api "scheduledtaskpage.js", "scheduledtaskspage.js", "search.js", - "supporterkeypage.js", "syncactivity.js", "syncsettings.js", "thememediaplayer.js", -- cgit v1.2.3 From a100effdb6f63923debaeb4d12098d711918f7ab Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 21 May 2015 00:16:05 -0400 Subject: escape single quotes --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'MediaBrowser.Api/Playback/BaseStreamingService.cs') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 6f1edb165..986dc4d2e 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -716,7 +716,7 @@ namespace MediaBrowser.Api.Playback // TODO: Perhaps also use original_size=1920x800 ?? return string.Format("subtitles=filename='{0}'{1},setpts=PTS -{2}/TB", - subtitlePath.Replace('\\', '/').Replace(":/", "\\:/"), + subtitlePath.Replace("'", "\\'").Replace('\\', '/').Replace(":/", "\\:/"), charsetParam, seconds.ToString(UsCulture)); } @@ -724,7 +724,7 @@ namespace MediaBrowser.Api.Playback var mediaPath = state.MediaPath ?? string.Empty; return string.Format("subtitles='{0}:si={1}',setpts=PTS -{2}/TB", - mediaPath.Replace('\\', '/').Replace(":/", "\\:/"), + mediaPath.Replace("'", "\\'").Replace('\\', '/').Replace(":/", "\\:/"), state.InternalSubtitleStreamOffset.ToString(UsCulture), seconds.ToString(UsCulture)); } -- cgit v1.2.3 From 11aa20a7b819f54551256467dcdf315dbdef4237 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 21 May 2015 16:53:14 -0400 Subject: hls updates --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 2 +- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 19 ++++++++++++------- .../Library/CoreResolutionIgnoreRule.cs | 1 + .../Localization/Server/server.json | 1 + MediaBrowser.WebDashboard/Api/DashboardService.cs | 13 +++++++++++++ MediaBrowser.WebDashboard/Api/PackageCreator.cs | 14 ++++++++++---- MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs | 2 +- 7 files changed, 39 insertions(+), 13 deletions(-) (limited to 'MediaBrowser.Api/Playback/BaseStreamingService.cs') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 986dc4d2e..a36b65f74 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -179,7 +179,7 @@ namespace MediaBrowser.Api.Playback if (time > _slowSeekTicks && _slowSeekTicks > 0) { - return string.Format("-ss {0}", MediaEncoder.GetTimeParameter(_slowSeekTicks)); + return string.Format("-ss {0}", MediaEncoder.GetTimeParameter(time)); } return string.Empty; diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 2d8b71a0c..45d560da7 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -244,7 +244,7 @@ namespace MediaBrowser.Api.Playback.Hls } catch (FileNotFoundException) { - + } } @@ -675,7 +675,7 @@ namespace MediaBrowser.Api.Playback.Hls builder.AppendLine("#EXTM3U"); builder.AppendLine("#EXT-X-VERSION:3"); - builder.AppendLine("#EXT-X-TARGETDURATION:" + state.SegmentLength.ToString(UsCulture)); + builder.AppendLine("#EXT-X-TARGETDURATION:" + (state.SegmentLength).ToString(UsCulture)); builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0"); var queryStringIndex = Request.RawUrl.IndexOf('?'); @@ -758,12 +758,14 @@ namespace MediaBrowser.Api.Playback.Hls } var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", - 1.ToString(UsCulture)); + state.SegmentLength.ToString(UsCulture)); var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; args += " " + GetVideoQualityParam(state, H264Encoder, true) + keyFrameArg; + //args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0"; + // Add resolution params, if specified if (!hasGraphicalSubs) { @@ -783,7 +785,7 @@ namespace MediaBrowser.Api.Playback.Hls { var threads = GetNumberOfThreads(state, false); - var inputModifier = GetInputModifier(state); + var inputModifier = GetInputModifier(state, false); // If isEncoding is true we're actually starting ffmpeg var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0"; @@ -799,7 +801,8 @@ namespace MediaBrowser.Api.Playback.Hls if (endTime < state.RunTimeTicks.Value) { - toTimeParam = " -to " + MediaEncoder.GetTimeParameter(endTime); + //toTimeParam = " -to " + MediaEncoder.GetTimeParameter(endTime); + toTimeParam = " -t " + MediaEncoder.GetTimeParameter(TimeSpan.FromSeconds(durationSeconds).Ticks); } } @@ -809,11 +812,13 @@ namespace MediaBrowser.Api.Playback.Hls slowSeekParam = " " + slowSeekParam; } + //state.EnableGenericHlsSegmenter = true; + if (state.EnableGenericHlsSegmenter) { var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d.ts"; - return string.Format("{0} {1}{10}{11} -map_metadata -1 -threads {2} {3} {4} -flags -global_header -sc_threshold 0 {5} -f segment -segment_time {6} -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", + return string.Format("{0} {11} {1}{10} -map_metadata -1 -threads {2} {3} {4} -flags -global_header -sc_threshold 0 {5} -f segment -segment_time {6} -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", inputModifier, GetInputArgument(state), threads, @@ -829,7 +834,7 @@ namespace MediaBrowser.Api.Playback.Hls ).Trim(); } - return string.Format("{0} {1}{10}{11} -map_metadata -1 -threads {2} {3} {4} -flags -global_header -copyts -sc_threshold 0 {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", + return string.Format("{0}{11} {1}{10} -map_metadata -1 -threads {2} {3} {4} -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0) + " -flags -global_header -sc_threshold 0 {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", inputModifier, GetInputArgument(state), threads, diff --git a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index 3dda3f158..be8c1cfbd 100644 --- a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -32,6 +32,7 @@ namespace MediaBrowser.Server.Implementations.Library ".wd_tv", // Synology + "@eaDir", "eaDir" }; diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index 8f90f1aca..579df55a8 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -1243,6 +1243,7 @@ "OptionNoThemeVideo": "No Theme Video", "LabelOneTimeDonationAmount": "Donation amount:", "ButtonDonate": "Donate", + "ButtonPurchase": "Purchase", "OptionActor": "Actor", "OptionComposer": "Composer", "OptionDirector": "Director", diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 07d9ed497..715f06055 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -320,6 +320,19 @@ namespace MediaBrowser.WebDashboard.Api await DumpFile(filename, Path.Combine(destination, filename), mode, culture, appVersion).ConfigureAwait(false); } + + var excludeFiles = new List(); + + if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase)) + { + excludeFiles.Add("supporter.html"); + excludeFiles.Add("supporterkey.html"); + } + + foreach (var file in excludeFiles) + { + File.Delete(Path.Combine(destination, file)); + } } private async Task DumpJs(string source, string mode, string destination, string culture, string appVersion) diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 2823733ea..a414e4131 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -291,7 +291,7 @@ namespace MediaBrowser.WebDashboard.Api html = ReplaceBetween(html, "", "", string.Empty); // Replace CORDOVA_REPLACE_SUPPORTER_SUBMIT_START - html = ReplaceBetween(html, "", "", "${ButtonDonate}"); + html = ReplaceBetween(html, "", "", "${ButtonPurchase}"); return html; } @@ -299,14 +299,20 @@ namespace MediaBrowser.WebDashboard.Api private string ReplaceBetween(string html, string startToken, string endToken, string newHtml) { var start = html.IndexOf(startToken, StringComparison.OrdinalIgnoreCase); - var end = html.IndexOf(endToken, StringComparison.OrdinalIgnoreCase); - if (start == -1 || end == -1) + if (start == -1) { return html; } - string result = html.Substring(start + 1, end - start - 1); + var end = html.IndexOf(endToken, start, StringComparison.OrdinalIgnoreCase); + + if (end == -1) + { + return html; + } + + string result = html.Substring(start, end - start); html = html.Replace(result, newHtml); return ReplaceBetween(html, startToken, endToken, newHtml); diff --git a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs index b2d276b73..ddbd528d9 100644 --- a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs @@ -33,7 +33,7 @@ namespace MediaBrowser.XbmcMetadata.Savers { var path = item.ContainingFolderPath; - list.Add(Path.Combine(path, "VIDEO_TS.nfo")); + list.Add(Path.Combine(path, "VIDEO_TS", "VIDEO_TS.nfo")); } if (item.VideoType == VideoType.Dvd || item.VideoType == VideoType.BluRay || item.VideoType == VideoType.HdDvd) -- cgit v1.2.3 From f26a639a36aed431a9090fb833358871f2192e74 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 24 May 2015 14:33:28 -0400 Subject: fix audio-only hls --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 26 --- MediaBrowser.Api/Playback/Hls/BaseHlsService.cs | 4 +- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 256 ++++++++++++++------- MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs | 35 ++- MediaBrowser.Api/Playback/Hls/VideoHlsService.cs | 21 +- .../Playback/Progressive/VideoService.cs | 2 +- MediaBrowser.Api/Playback/StreamState.cs | 7 +- MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs | 2 +- MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml | 2 +- MediaBrowser.Model/Dlna/StreamInfo.cs | 5 + .../IO/LibraryMonitor.cs | 6 +- SharedVersion.cs | 4 +- 12 files changed, 221 insertions(+), 149 deletions(-) (limited to 'MediaBrowser.Api/Playback/BaseStreamingService.cs') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index a36b65f74..0b8f21129 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -148,7 +148,6 @@ namespace MediaBrowser.Api.Playback } protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - private readonly long _slowSeekTicks = TimeSpan.FromSeconds(0).Ticks; /// /// Gets the fast seek command line parameter. @@ -161,23 +160,6 @@ namespace MediaBrowser.Api.Playback var time = request.StartTimeTicks ?? 0; if (time > 0) - { - if (time > _slowSeekTicks && EnableSlowSeek) - { - time -= _slowSeekTicks; - } - - return string.Format("-ss {0}", MediaEncoder.GetTimeParameter(time)); - } - - return string.Empty; - } - - protected string GetSlowSeekCommandLineParameter(StreamRequest request) - { - var time = request.StartTimeTicks ?? 0; - - if (time > _slowSeekTicks && _slowSeekTicks > 0) { return string.Format("-ss {0}", MediaEncoder.GetTimeParameter(time)); } @@ -185,14 +167,6 @@ namespace MediaBrowser.Api.Playback return string.Empty; } - protected virtual bool EnableSlowSeek - { - get - { - return false; - } - } - /// /// Gets the map args. /// diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index a143da772..b2ffeca3d 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -134,7 +134,7 @@ namespace MediaBrowser.Api.Playback.Hls var appendBaselineStream = false; var baselineStreamBitrate = 64000; - var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream; + var hlsVideoRequest = state.VideoRequest as GetHlsVideoStreamLegacy; if (hlsVideoRequest != null) { appendBaselineStream = hlsVideoRequest.AppendBaselineStream; @@ -245,7 +245,7 @@ namespace MediaBrowser.Api.Playback.Hls protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding) { - var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream; + var hlsVideoRequest = state.VideoRequest as GetHlsVideoStreamLegacy; var itsOffsetMs = hlsVideoRequest == null ? 0 diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index a2e327f7d..6774cc859 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -30,27 +30,60 @@ namespace MediaBrowser.Api.Playback.Hls /// [Route("/Videos/{Id}/master.m3u8", "GET", Summary = "Gets a video stream using HTTP live streaming.")] [Route("/Videos/{Id}/master.m3u8", "HEAD", Summary = "Gets a video stream using HTTP live streaming.")] - public class GetMasterHlsVideoStream : VideoStreamRequest + public class GetMasterHlsVideoPlaylist : VideoStreamRequest, IMasterHlsRequest { public bool EnableAdaptiveBitrateStreaming { get; set; } - public GetMasterHlsVideoStream() + public GetMasterHlsVideoPlaylist() { EnableAdaptiveBitrateStreaming = true; } } + [Route("/Audio/{Id}/master.m3u8", "GET", Summary = "Gets an audio stream using HTTP live streaming.")] + [Route("/Audio/{Id}/master.m3u8", "HEAD", Summary = "Gets an audio stream using HTTP live streaming.")] + public class GetMasterHlsAudioPlaylist : StreamRequest, IMasterHlsRequest + { + public bool EnableAdaptiveBitrateStreaming { get; set; } + + public GetMasterHlsAudioPlaylist() + { + EnableAdaptiveBitrateStreaming = true; + } + } + + public interface IMasterHlsRequest + { + bool EnableAdaptiveBitrateStreaming { get; set; } + } + [Route("/Videos/{Id}/main.m3u8", "GET", Summary = "Gets a video stream using HTTP live streaming.")] - public class GetMainHlsVideoStream : VideoStreamRequest + public class GetVariantHlsVideoPlaylist : VideoStreamRequest + { + } + + [Route("/Audio/{Id}/main.m3u8", "GET", Summary = "Gets an audio stream using HTTP live streaming.")] + public class GetVariantHlsAudioPlaylist : StreamRequest { } - /// - /// Class GetHlsVideoSegment - /// [Route("/Videos/{Id}/hlsdynamic/{PlaylistId}/{SegmentId}.ts", "GET")] [Api(Description = "Gets an Http live streaming segment file. Internal use only.")] - public class GetDynamicHlsVideoSegment : VideoStreamRequest + public class GetHlsVideoSegment : VideoStreamRequest + { + public string PlaylistId { get; set; } + + /// + /// Gets or sets the segment id. + /// + /// The segment id. + public string SegmentId { get; set; } + } + + [Route("/Audio/{Id}/hlsdynamic/{PlaylistId}/{SegmentId}.aac", "GET")] + [Route("/Audio/{Id}/hlsdynamic/{PlaylistId}/{SegmentId}.ts", "GET")] + [Api(Description = "Gets an Http live streaming segment file. Internal use only.")] + public class GetHlsAudioSegment : StreamRequest { public string PlaylistId { get; set; } @@ -71,27 +104,47 @@ namespace MediaBrowser.Api.Playback.Hls protected INetworkManager NetworkManager { get; private set; } - public Task Get(GetMasterHlsVideoStream request) + public Task Get(GetMasterHlsVideoPlaylist request) { - return GetAsync(request, "GET"); + return GetMasterPlaylistInternal(request, "GET"); } - public Task Head(GetMasterHlsVideoStream request) + public Task Head(GetMasterHlsVideoPlaylist request) { - return GetAsync(request, "HEAD"); + return GetMasterPlaylistInternal(request, "HEAD"); } - public Task Get(GetMainHlsVideoStream request) + public Task Get(GetMasterHlsAudioPlaylist request) { - return GetPlaylistAsync(request, "main"); + return GetMasterPlaylistInternal(request, "GET"); } - public Task Get(GetDynamicHlsVideoSegment request) + public Task Head(GetMasterHlsAudioPlaylist request) + { + return GetMasterPlaylistInternal(request, "HEAD"); + } + + public Task Get(GetVariantHlsVideoPlaylist request) + { + return GetVariantPlaylistInternal(request, true, "main"); + } + + public Task Get(GetVariantHlsAudioPlaylist request) + { + return GetVariantPlaylistInternal(request, false, "main"); + } + + public Task Get(GetHlsVideoSegment request) + { + return GetDynamicSegment(request, request.SegmentId); + } + + public Task Get(GetHlsAudioSegment request) { return GetDynamicSegment(request, request.SegmentId); } - private async Task GetDynamicSegment(VideoStreamRequest request, string segmentId) + private async Task GetDynamicSegment(StreamRequest request, string segmentId) { if ((request.StartTimeTicks ?? 0) > 0) { @@ -107,7 +160,7 @@ namespace MediaBrowser.Api.Playback.Hls var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8"); - var segmentPath = GetSegmentPath(playlistPath, requestedIndex); + var segmentPath = GetSegmentPath(state, playlistPath, requestedIndex); var segmentLength = state.SegmentLength; var segmentExtension = GetSegmentFileExtension(state); @@ -191,11 +244,11 @@ namespace MediaBrowser.Api.Playback.Hls ApiEntryPoint.Instance.TranscodingStartLock.Release(); } - Logger.Info("waiting for {0}", segmentPath); - while (!File.Exists(segmentPath)) - { - await Task.Delay(50, cancellationToken).ConfigureAwait(false); - } + //Logger.Info("waiting for {0}", segmentPath); + //while (!File.Exists(segmentPath)) + //{ + // await Task.Delay(50, cancellationToken).ConfigureAwait(false); + //} Logger.Info("returning {0}", segmentPath); job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); @@ -254,7 +307,7 @@ namespace MediaBrowser.Api.Playback.Hls for (var i = 0; i < requestedIndex; i++) { - var segmentPath = GetSegmentPath(playlist, i); + var segmentPath = GetSegmentPath(state, playlist, i); double length; if (SegmentLengths.TryGetValue(Path.GetFileName(segmentPath), out length)) @@ -360,7 +413,7 @@ namespace MediaBrowser.Api.Playback.Hls { var segmentId = "0"; - var segmentRequest = request as GetDynamicHlsVideoSegment; + var segmentRequest = request as GetHlsVideoSegment; if (segmentRequest != null) { segmentId = segmentRequest.SegmentId; @@ -369,13 +422,13 @@ namespace MediaBrowser.Api.Playback.Hls return int.Parse(segmentId, NumberStyles.Integer, UsCulture); } - private string GetSegmentPath(string playlist, int index) + private string GetSegmentPath(StreamState state, string playlist, int index) { var folder = Path.GetDirectoryName(playlist); var filename = Path.GetFileNameWithoutExtension(playlist); - return Path.Combine(folder, filename + index.ToString(UsCulture) + ".ts"); + return Path.Combine(folder, filename + index.ToString(UsCulture) + GetSegmentFileExtension(state)); } private async Task GetSegmentResult(string playlistPath, @@ -474,7 +527,7 @@ namespace MediaBrowser.Api.Playback.Hls }); } - private async Task GetAsync(GetMasterHlsVideoStream request, string method) + private async Task GetMasterPlaylistInternal(StreamRequest request, string method) { var state = await GetState(request, CancellationToken.None).ConfigureAwait(false); @@ -511,14 +564,16 @@ namespace MediaBrowser.Api.Playback.Hls var playlistUrl = isLiveStream ? "live.m3u8" : "main.m3u8"; playlistUrl += queryString; - var request = (GetMasterHlsVideoStream)state.Request; + var request = state.Request; var subtitleStreams = state.MediaSource .MediaStreams .Where(i => i.IsTextSubtitleStream) .ToList(); - var subtitleGroup = subtitleStreams.Count > 0 && request.SubtitleMethod == SubtitleDeliveryMethod.Hls ? + var subtitleGroup = subtitleStreams.Count > 0 && + (request is GetMasterHlsVideoPlaylist) && + ((GetMasterHlsVideoPlaylist)request).SubtitleMethod == SubtitleDeliveryMethod.Hls ? "subs" : null; @@ -526,7 +581,7 @@ namespace MediaBrowser.Api.Playback.Hls if (EnableAdaptiveBitrateStreaming(state, isLiveStream)) { - var requestedVideoBitrate = state.VideoRequest.VideoBitRate.Value; + var requestedVideoBitrate = state.VideoRequest == null ? 0 : state.VideoRequest.VideoBitRate ?? 0; // By default, vary by just 200k var variation = GetBitrateVariation(totalBitrate); @@ -596,7 +651,7 @@ namespace MediaBrowser.Api.Playback.Hls return false; } - var request = state.Request as GetMasterHlsVideoStream; + var request = state.Request as IMasterHlsRequest; if (request != null && !request.EnableAdaptiveBitrateStreaming) { return false; @@ -618,6 +673,11 @@ namespace MediaBrowser.Api.Playback.Hls return false; } + if (!state.IsOutputVideo) + { + return false; + } + // Having problems in android return false; //return state.VideoRequest.VideoBitRate.HasValue; @@ -673,7 +733,7 @@ namespace MediaBrowser.Api.Playback.Hls return variation; } - private async Task GetPlaylistAsync(VideoStreamRequest request, string name) + private async Task GetVariantPlaylistInternal(StreamRequest request, bool isOutputVideo, string name) { var state = await GetState(request, CancellationToken.None).ConfigureAwait(false); @@ -697,10 +757,11 @@ namespace MediaBrowser.Api.Playback.Hls builder.AppendLine("#EXTINF:" + length.ToString(UsCulture) + ","); - builder.AppendLine(string.Format("hlsdynamic/{0}/{1}.ts{2}", + builder.AppendLine(string.Format("hlsdynamic/{0}/{1}{2}{3}", name, index.ToString(UsCulture), + GetSegmentFileExtension(isOutputVideo), queryString)); seconds -= state.SegmentLength; @@ -716,6 +777,28 @@ namespace MediaBrowser.Api.Playback.Hls protected override string GetAudioArguments(StreamState state) { + if (!state.IsOutputVideo) + { + var audioTranscodeParams = new List(); + if (state.OutputAudioBitrate.HasValue) + { + audioTranscodeParams.Add("-ab " + state.OutputAudioBitrate.Value.ToString(UsCulture)); + } + + if (state.OutputAudioChannels.HasValue) + { + audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(UsCulture)); + } + + if (state.OutputAudioSampleRate.HasValue) + { + audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture)); + } + + audioTranscodeParams.Add("-vn"); + return string.Join(" ", audioTranscodeParams.ToArray()); + } + var codec = state.OutputAudioCodec; if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase)) @@ -746,6 +829,11 @@ namespace MediaBrowser.Api.Playback.Hls protected override string GetVideoArguments(StreamState state) { + if (!state.IsOutputVideo) + { + return string.Empty; + } + var codec = state.OutputVideoCodec; var args = "-codec:v:0 " + codec; @@ -758,31 +846,35 @@ namespace MediaBrowser.Api.Playback.Hls // See if we can save come cpu cycles by avoiding encoding if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) { - return state.VideoStream != null && IsH264(state.VideoStream) ? - args + " -bsf:v h264_mp4toannexb" : - args; + args += state.VideoStream != null && IsH264(state.VideoStream) + ? args + " -bsf:v h264_mp4toannexb" + : args; } + else + { + var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", + state.SegmentLength.ToString(UsCulture)); - var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", - state.SegmentLength.ToString(UsCulture)); + var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; - var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream; + args += " " + GetVideoQualityParam(state, H264Encoder, true) + keyFrameArg; - args += " " + GetVideoQualityParam(state, H264Encoder, true) + keyFrameArg; + //args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0"; - //args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0"; + // Add resolution params, if specified + if (!hasGraphicalSubs) + { + args += GetOutputSizeParam(state, codec, false); + } - // Add resolution params, if specified - if (!hasGraphicalSubs) - { - args += GetOutputSizeParam(state, codec, false); + // This is for internal graphical subs + if (hasGraphicalSubs) + { + args += GetGraphicalSubtitleParam(state, codec); + } } - // This is for internal graphical subs - if (hasGraphicalSubs) - { - args += GetGraphicalSubtitleParam(state, codec); - } + args += " -flags +loop-global_header -sc_threshold 0"; return args; } @@ -797,7 +889,7 @@ namespace MediaBrowser.Api.Playback.Hls var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0"; var toTimeParam = string.Empty; - if (state.RunTimeTicks.HasValue) + if (state.RunTimeTicks.HasValue && state.IsOutputVideo) { var startTime = state.Request.StartTimeTicks ?? 0; var durationSeconds = ApiEntryPoint.Instance.GetEncodingOptions().ThrottleThresholdInSeconds; @@ -812,46 +904,43 @@ namespace MediaBrowser.Api.Playback.Hls } } - var slowSeekParam = GetSlowSeekCommandLineParameter(state.Request); - if (!string.IsNullOrWhiteSpace(slowSeekParam)) + var timestampOffsetParam = string.Empty; + if (state.IsOutputVideo) { - slowSeekParam = " " + slowSeekParam; + timestampOffsetParam = " -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0).ToString(CultureInfo.InvariantCulture); } + + var mapArgs = state.IsOutputVideo ? GetMapArgs(state) : string.Empty; - //state.EnableGenericHlsSegmenter = true; + //var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state); - if (state.EnableGenericHlsSegmenter) - { - var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d.ts"; + //return string.Format("{0} {11} {1}{10} -map_metadata -1 -threads {2} {3} {4} {5} -f segment -segment_time {6} -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", + // inputModifier, + // GetInputArgument(state), + // threads, + // mapArgs, + // GetVideoArguments(state), + // GetAudioArguments(state), + // state.SegmentLength.ToString(UsCulture), + // startNumberParam, + // outputPath, + // outputTsArg, + // slowSeekParam, + // toTimeParam + // ).Trim(); - return string.Format("{0} {11} {1}{10} -map_metadata -1 -threads {2} {3} {4} -flags -global_header -sc_threshold 0 {5} -f segment -segment_time {6} -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", - inputModifier, - GetInputArgument(state), - threads, - GetMapArgs(state), - GetVideoArguments(state), - GetAudioArguments(state), - state.SegmentLength.ToString(UsCulture), - startNumberParam, - outputPath, - outputTsArg, - slowSeekParam, - toTimeParam - ).Trim(); - } - - return string.Format("{0}{11} {1}{10} -map_metadata -1 -threads {2} {3} {4} -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0) + " -flags -global_header -sc_threshold 0 {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", + return string.Format("{0}{11} {1} -map_metadata -1 -threads {2} {3} {4}{5} {6} -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"", inputModifier, GetInputArgument(state), threads, - GetMapArgs(state), + mapArgs, GetVideoArguments(state), + timestampOffsetParam, GetAudioArguments(state), state.SegmentLength.ToString(UsCulture), startNumberParam, state.HlsListSize.ToString(UsCulture), outputPath, - slowSeekParam, toTimeParam ).Trim(); } @@ -872,14 +961,6 @@ namespace MediaBrowser.Api.Playback.Hls } } - protected override bool EnableSlowSeek - { - get - { - return true; - } - } - /// /// Gets the segment file extension. /// @@ -887,7 +968,12 @@ namespace MediaBrowser.Api.Playback.Hls /// System.String. protected override string GetSegmentFileExtension(StreamState state) { - return ".ts"; + return GetSegmentFileExtension(state.IsOutputVideo); + } + + protected string GetSegmentFileExtension(bool isOutputVideo) + { + return isOutputVideo ? ".ts" : ".ts"; } } } diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs index 5d8c67abe..b44d7f660 100644 --- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs +++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs @@ -14,8 +14,10 @@ namespace MediaBrowser.Api.Playback.Hls [Route("/Audio/{Id}/hls/{SegmentId}/stream.mp3", "GET")] [Route("/Audio/{Id}/hls/{SegmentId}/stream.aac", "GET")] [Api(Description = "Gets an Http live streaming segment file. Internal use only.")] - public class GetHlsAudioSegment + public class GetHlsAudioSegmentLegacy { + // TODO: Deprecate with new iOS app + /// /// Gets or sets the id. /// @@ -29,12 +31,31 @@ namespace MediaBrowser.Api.Playback.Hls public string SegmentId { get; set; } } + /// + /// Class GetHlsVideoStream + /// + [Route("/Videos/{Id}/stream.m3u8", "GET")] + [Api(Description = "Gets a video stream using HTTP live streaming.")] + public class GetHlsVideoStreamLegacy : VideoStreamRequest + { + // TODO: Deprecate with new iOS app + + [ApiMember(Name = "BaselineStreamAudioBitRate", Description = "Optional. Specify the audio bitrate for the baseline stream.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? BaselineStreamAudioBitRate { get; set; } + + [ApiMember(Name = "AppendBaselineStream", Description = "Optional. Whether or not to include a baseline audio-only stream in the master playlist.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool AppendBaselineStream { get; set; } + + [ApiMember(Name = "TimeStampOffsetMs", Description = "Optional. Alter the timestamps in the playlist by a given amount, in ms. Default is 1000.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int TimeStampOffsetMs { get; set; } + } + /// /// Class GetHlsVideoSegment /// [Route("/Videos/{Id}/hls/{PlaylistId}/stream.m3u8", "GET")] [Api(Description = "Gets an Http live streaming segment file. Internal use only.")] - public class GetHlsPlaylist + public class GetHlsPlaylistLegacy { // TODO: Deprecate with new iOS app @@ -63,8 +84,10 @@ namespace MediaBrowser.Api.Playback.Hls /// [Route("/Videos/{Id}/hls/{PlaylistId}/{SegmentId}.ts", "GET")] [Api(Description = "Gets an Http live streaming segment file. Internal use only.")] - public class GetHlsVideoSegment : VideoStreamRequest + public class GetHlsVideoSegmentLegacy : VideoStreamRequest { + // TODO: Deprecate with new iOS app + public string PlaylistId { get; set; } /// @@ -85,7 +108,7 @@ namespace MediaBrowser.Api.Playback.Hls _config = config; } - public object Get(GetHlsPlaylist request) + public object Get(GetHlsPlaylistLegacy request) { var file = request.PlaylistId + Path.GetExtension(Request.PathInfo); file = Path.Combine(_appPaths.TranscodingTempPath, file); @@ -103,7 +126,7 @@ namespace MediaBrowser.Api.Playback.Hls /// /// The request. /// System.Object. - public object Get(GetHlsVideoSegment request) + public object Get(GetHlsVideoSegmentLegacy request) { var file = request.SegmentId + Path.GetExtension(Request.PathInfo); file = Path.Combine(_config.ApplicationPaths.TranscodingTempPath, file); @@ -121,7 +144,7 @@ namespace MediaBrowser.Api.Playback.Hls /// /// The request. /// System.Object. - public object Get(GetHlsAudioSegment request) + public object Get(GetHlsAudioSegmentLegacy request) { // TODO: Deprecate with new iOS app var file = request.SegmentId + Path.GetExtension(Request.PathInfo); diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 626df59f2..f21be190f 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -11,25 +11,6 @@ using System; namespace MediaBrowser.Api.Playback.Hls { - /// - /// Class GetHlsVideoStream - /// - [Route("/Videos/{Id}/stream.m3u8", "GET")] - [Api(Description = "Gets a video stream using HTTP live streaming.")] - public class GetHlsVideoStream : VideoStreamRequest - { - // TODO: Deprecate with new iOS app - - [ApiMember(Name = "BaselineStreamAudioBitRate", Description = "Optional. Specify the audio bitrate for the baseline stream.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? BaselineStreamAudioBitRate { get; set; } - - [ApiMember(Name = "AppendBaselineStream", Description = "Optional. Whether or not to include a baseline audio-only stream in the master playlist.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool AppendBaselineStream { get; set; } - - [ApiMember(Name = "TimeStampOffsetMs", Description = "Optional. Alter the timestamps in the playlist by a given amount, in ms. Default is 1000.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int TimeStampOffsetMs { get; set; } - } - [Route("/Videos/{Id}/live.m3u8", "GET")] [Api(Description = "Gets a video stream using HTTP live streaming.")] public class GetLiveHlsStream : VideoStreamRequest @@ -50,7 +31,7 @@ namespace MediaBrowser.Api.Playback.Hls /// /// The request. /// System.Object. - public object Get(GetHlsVideoStream request) + public object Get(GetHlsVideoStreamLegacy request) { return ProcessRequest(request, false); } diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 27482c50c..283f9671f 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -15,7 +15,7 @@ using System.IO; namespace MediaBrowser.Api.Playback.Progressive { /// - /// Class GetAudioStream + /// Class GetVideoStream /// [Route("/Videos/{Id}/stream.ts", "GET")] [Route("/Videos/{Id}/stream.webm", "GET")] diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index 2d1e896db..02b7720a4 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -41,7 +41,7 @@ namespace MediaBrowser.Api.Playback public string InputContainer { get; set; } public MediaSourceInfo MediaSource { get; set; } - + public MediaStream AudioStream { get; set; } public MediaStream VideoStream { get; set; } public MediaStream SubtitleStream { get; set; } @@ -57,6 +57,10 @@ namespace MediaBrowser.Api.Playback public MediaProtocol InputProtocol { get; set; } + public bool IsOutputVideo + { + get { return Request is VideoStreamRequest; } + } public bool IsInputVideo { get; set; } public bool IsInputArchive { get; set; } @@ -66,7 +70,6 @@ namespace MediaBrowser.Api.Playback public List PlayableStreamFileNames { get; set; } public int SegmentLength = 3; - public bool EnableGenericHlsSegmenter = false; public int HlsListSize { get diff --git a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs b/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs index a26c43911..63bb0b52a 100644 --- a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs +++ b/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.Dlna.Profiles Identification = new DeviceIdentification { - ModelName = "WD TV HD Live", + ModelName = "WD TV", Headers = new [] { diff --git a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml index 684e61c42..4f8000c3b 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml @@ -2,7 +2,7 @@ WDTV Live - WD TV HD Live + WD TV diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 645c1c7d0..92eb0372c 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -158,6 +158,11 @@ namespace MediaBrowser.Model.Dlna if (MediaType == DlnaProfileType.Audio) { + if (StringHelper.EqualsIgnoreCase(SubProtocol, "hls")) + { + return string.Format("{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); + } + return string.Format("{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); } diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index 2d4770fac..491549d64 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs @@ -82,9 +82,9 @@ namespace MediaBrowser.Server.Implementations.IO } // This is an arbitraty amount of time, but delay it because file system writes often trigger events after RemoveTempIgnore has been called. - // Seeing long delays in some situations, especially over the network. - // Seeing delays up to 40 seconds, but not going to ignore changes for that long. - await Task.Delay(5000).ConfigureAwait(false); + // Seeing long delays in some situations, especially over the network, sometimes up to 45 seconds + // But if we make this delay too high, we risk missing legitimate changes + await Task.Delay(10000).ConfigureAwait(false); string val; _tempIgnoredPaths.TryRemove(path, out val); diff --git a/SharedVersion.cs b/SharedVersion.cs index 653297d70..2a2ce1e51 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; -//[assembly: AssemblyVersion("3.0.*")] -[assembly: AssemblyVersion("3.0.5621.1")] +[assembly: AssemblyVersion("3.0.*")] +//[assembly: AssemblyVersion("3.0.5621.1")] -- cgit v1.2.3 From 5048914445c371ced2f060b9bcd1d43945bcf8c0 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 24 May 2015 23:02:09 -0400 Subject: update hls --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 2 +- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'MediaBrowser.Api/Playback/BaseStreamingService.cs') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 0b8f21129..1b7bcab53 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1077,7 +1077,7 @@ namespace MediaBrowser.Api.Playback { get { - return true; + return false; } } diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 6774cc859..72d903a0a 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -889,7 +889,7 @@ namespace MediaBrowser.Api.Playback.Hls var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0"; var toTimeParam = string.Empty; - if (state.RunTimeTicks.HasValue && state.IsOutputVideo) + if (state.RunTimeTicks.HasValue && state.IsOutputVideo && ApiEntryPoint.Instance.GetEncodingOptions().EnableThrottling) { var startTime = state.Request.StartTimeTicks ?? 0; var durationSeconds = ApiEntryPoint.Instance.GetEncodingOptions().ThrottleThresholdInSeconds; -- cgit v1.2.3 From 541d0c2ce174fc587a07ceb88df0d65656ef122a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 25 May 2015 13:32:22 -0400 Subject: 3.0.5621.2 --- MediaBrowser.Api/ApiEntryPoint.cs | 6 - MediaBrowser.Api/Playback/BaseStreamingService.cs | 9 +- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 13 +- .../Localization/JavaScript/da.json | 1040 ++++++++++---------- .../Localization/Server/da.json | 114 +-- .../Localization/Server/fr.json | 2 +- .../Localization/Server/kk.json | 2 +- .../Localization/Server/pt-BR.json | 2 +- .../Localization/Server/ru.json | 2 +- .../Localization/Server/server.json | 5 +- MediaBrowser.WebDashboard/Api/PackageCreator.cs | 1 - .../MediaBrowser.WebDashboard.csproj | 3 + SharedVersion.cs | 4 +- 13 files changed, 602 insertions(+), 601 deletions(-) (limited to 'MediaBrowser.Api/Playback/BaseStreamingService.cs') diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index dff60d483..05ff503e4 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -349,12 +349,6 @@ namespace MediaBrowser.Api if (!string.IsNullOrWhiteSpace(job.PlaySessionId)) { timerDuration = 60000; - - // With newer just in time encoding, we no longer need to be aggressive about killing the stream - if (!job.IsLiveOutput) - { - timerDuration = 180000; - } } } diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 1b7bcab53..31679aad3 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1060,7 +1060,7 @@ namespace MediaBrowser.Api.Playback private void StartThrottler(StreamState state, TranscodingJob transcodingJob) { - if (EnableThrottling && state.InputProtocol == MediaProtocol.File && + if (EnableThrottling(state) && state.InputProtocol == MediaProtocol.File && state.RunTimeTicks.HasValue && state.VideoType == VideoType.VideoFile && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) @@ -1073,12 +1073,9 @@ namespace MediaBrowser.Api.Playback } } - protected virtual bool EnableThrottling + protected virtual bool EnableThrottling(StreamState state) { - get - { - return false; - } + return true; } private async void StartStreamingLog(TranscodingJob transcodingJob, StreamState state, Stream source, Stream target) diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 72d903a0a..d2b276ba5 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -889,7 +889,7 @@ namespace MediaBrowser.Api.Playback.Hls var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0"; var toTimeParam = string.Empty; - if (state.RunTimeTicks.HasValue && state.IsOutputVideo && ApiEntryPoint.Instance.GetEncodingOptions().EnableThrottling) + if (EnableSplitTranscoding(state)) { var startTime = state.Request.StartTimeTicks ?? 0; var durationSeconds = ApiEntryPoint.Instance.GetEncodingOptions().ThrottleThresholdInSeconds; @@ -945,12 +945,19 @@ namespace MediaBrowser.Api.Playback.Hls ).Trim(); } - protected override bool EnableThrottling + protected override bool EnableThrottling(StreamState state) { - get + return !EnableSplitTranscoding(state); + } + + private bool EnableSplitTranscoding(StreamState state) + { + if (string.Equals(Request.QueryString["EnableSplitTranscoding"], "false", StringComparison.OrdinalIgnoreCase)) { return false; } + + return state.RunTimeTicks.HasValue && state.IsOutputVideo; } protected override bool EnableStreamCopy diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/da.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/da.json index bc3acfb24..f2a43c211 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/da.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/da.json @@ -36,7 +36,7 @@ "MessageKeyUpdated": "Tak. Din supporter n\u00f8gle er nu opdateret.", "MessageKeyRemoved": "Tak. Din supporter n\u00f8gle er fjernet.", "HeaderSupportTheTeam": "St\u00f8t Emby-holdet", - "TextEnjoyBonusFeatures": "Enjoy Bonus Features", + "TextEnjoyBonusFeatures": "F\u00e5 bonus funktioner", "TitleLiveTV": "Live TV", "TitleSync": "Sync", "ButtonDonate": "Don\u00e9r", @@ -95,84 +95,84 @@ "ButtonAddToCollection": "Tilf\u00f8j til samling", "HeaderSelectCertificatePath": "V\u00e6lg certifikatsti", "ConfirmMessageScheduledTaskButton": "Denne operation k\u00f8rer normalt som en planlagt opgave. Den kan ogs\u00e5 k\u00f8res manuelt her. For at konfigurere den planlage opgave, se:", - "HeaderSupporterBenefit": "A supporter membership provides additional benefits such as access to sync, premium plugins, internet channel content, and more. {0}Learn more{1}.", + "HeaderSupporterBenefit": "Med et supporter medlemskab opn\u00e5r du ekstra fordele s\u00e5 som adgang til sync, premium plugins, internet kanaler samt meget mere. {0}L\u00e6r mere{1}.", "LabelSyncNoTargetsHelp": "Det ser ud til at du for \u00f8jeblikket ikke har nogle enheder ser underst\u00f8tter sync.", "HeaderWelcomeToProjectServerDashboard": "Velkommen til Emby betjeningspanel", "HeaderWelcomeToProjectWebClient": "Velkommen til Emby", "ButtonTakeTheTour": "Vis introduktion", "HeaderWelcomeBack": "Velkommen tilbage!", "TitlePlugins": "Tilf\u00f8jelser", - "ButtonTakeTheTourToSeeWhatsNew": "Take the tour to see what's new", - "MessageNoSyncJobsFound": "No sync jobs found. Create sync jobs using the Sync buttons found throughout the web interface.", + "ButtonTakeTheTourToSeeWhatsNew": "Tag en rundvisning for at se hvad der er nyt", + "MessageNoSyncJobsFound": "Intet sync job blev fundet. Opret sync jobs ved at benytte Sync knapper som findes gennem web-interfacet.", "HeaderLibraryAccess": "Adgang til biblioteker", "HeaderChannelAccess": "Adgang til kanaler", "HeaderDeviceAccess": "Enhedsadgang", - "HeaderSelectDevices": "Select Devices", - "ButtonCancelItem": "Cancel item", - "ButtonQueueForRetry": "Queue for retry", - "ButtonReenable": "Re-enable", + "HeaderSelectDevices": "V\u00e6lg enheder", + "ButtonCancelItem": "Annuller genstand", + "ButtonQueueForRetry": "S\u00e6t et nyt fors\u00f8g i k\u00f8", + "ButtonReenable": "Genaktiver", "ButtonLearnMore": "L\u00e6r mere", - "SyncJobItemStatusSyncedMarkForRemoval": "Marked for removal", - "LabelAbortedByServerShutdown": "(Aborted by server shutdown)", - "LabelScheduledTaskLastRan": "Last ran {0}, taking {1}.", - "HeaderDeleteTaskTrigger": "Delete Task Trigger", + "SyncJobItemStatusSyncedMarkForRemoval": "Markeret til sletning", + "LabelAbortedByServerShutdown": "(Annulleret grundet server nedlukning)", + "LabelScheduledTaskLastRan": "Sidst k\u00f8rt {0}, og tog {1}.", + "HeaderDeleteTaskTrigger": "Slet Task Trigger", "HeaderTaskTriggers": "Task Triggers", - "MessageDeleteTaskTrigger": "Are you sure you wish to delete this task trigger?", - "MessageNoPluginsInstalled": "You have no plugins installed.", - "LabelVersionInstalled": "{0} installed", - "LabelNumberReviews": "{0} Reviews", - "LabelFree": "Free", - "HeaderPlaybackError": "Playback Error", - "MessagePlaybackErrorNotAllowed": "You're currently not authorized to play this content. Please contact your system administrator for details.", - "MessagePlaybackErrorNoCompatibleStream": "No compatible streams are currently available. Please try again later or contact your system administrator for details.", - "MessagePlaybackErrorRateLimitExceeded": "Your playback rate limit has been exceeded. Please contact your system administrator for details.", - "MessagePlaybackErrorPlaceHolder": "The content chosen is not playable from this device.", - "HeaderSelectAudio": "Select Audio", - "HeaderSelectSubtitles": "Select Subtitles", - "ButtonMarkForRemoval": "Remove from device", - "ButtonUnmarkForRemoval": "Cancel removal from device", - "LabelDefaultStream": "(Default)", - "LabelForcedStream": "(Forced)", - "LabelDefaultForcedStream": "(Default\/Forced)", - "LabelUnknownLanguage": "Unknown language", - "MessageConfirmSyncJobItemCancellation": "Are you sure you wish to cancel this item?", + "MessageDeleteTaskTrigger": "Er du sikker p\u00e5 du \u00f8nsker at slette denne task trigger?", + "MessageNoPluginsInstalled": "Du har ingen plugins installeret.", + "LabelVersionInstalled": "{0} installeret", + "LabelNumberReviews": "{0} Anmeldelser", + "LabelFree": "Gratis", + "HeaderPlaybackError": "Fejl i afspilning", + "MessagePlaybackErrorNotAllowed": "Du er p\u00e5 nuv\u00e6rende tidspunkt ikke autoriseret til at afspille dette indhold. Kontakt venligst din systemadministrator for flere detaljer.", + "MessagePlaybackErrorNoCompatibleStream": "Ingen kompatible streams er tilg\u00e6ngelige p\u00e5 nuv\u00e6rende tidspunkt. Pr\u00f8v igen senere eller kontakt din systemadministrator for flere detaljer.", + "MessagePlaybackErrorRateLimitExceeded": "Din afspilningskvote er blevet overskredet. Kontakt venligst din systemadministrator for flere detaljer.", + "MessagePlaybackErrorPlaceHolder": "Det valgte indhold kan ikke afspilles fra denne enhed.", + "HeaderSelectAudio": "V\u00e6lg lydspor", + "HeaderSelectSubtitles": "V\u00e6lg undertekster", + "ButtonMarkForRemoval": "Fjern fra enhed", + "ButtonUnmarkForRemoval": "Annuller fjernelse fra enhed", + "LabelDefaultStream": "(Standard)", + "LabelForcedStream": "(Tvungen)", + "LabelDefaultForcedStream": "(Standard\/Tvungen)", + "LabelUnknownLanguage": "Ukendt sprog", + "MessageConfirmSyncJobItemCancellation": "Er du sikker p\u00e5 du \u00f8nsker at annullere denne genstand?", "ButtonMute": "Lyd fra", - "ButtonUnmute": "Unmute", + "ButtonUnmute": "Sl\u00e5 lyd til", "ButtonStop": "Stop", "ButtonNextTrack": "N\u00e6ste spor", "ButtonPause": "Pause", "ButtonPlay": "Afspil", "ButtonEdit": "Rediger", - "ButtonQueue": "Queue", + "ButtonQueue": "K\u00f8", "ButtonPlayTrailer": "Afspil trailer", - "ButtonPlaylist": "Playlist", + "ButtonPlaylist": "Afspilningsliste", "ButtonPreviousTrack": "Forrige spor", - "LabelEnabled": "Enabled", - "LabelDisabled": "Disabled", - "ButtonMoreInformation": "More Information", - "LabelNoUnreadNotifications": "No unread notifications.", - "ButtonViewNotifications": "View notifications", - "ButtonMarkTheseRead": "Mark these read", + "LabelEnabled": "Sl\u00e5et til", + "LabelDisabled": "Sl\u00e5et fra", + "ButtonMoreInformation": "Mere information", + "LabelNoUnreadNotifications": "Ingen ul\u00e6ste notifikationer", + "ButtonViewNotifications": "Se notifikationer", + "ButtonMarkTheseRead": "Marker disse som l\u00e6st", "ButtonClose": "Luk", - "LabelAllPlaysSentToPlayer": "All plays will be sent to the selected player.", - "MessageInvalidUser": "Invalid username or password. Please try again.", - "HeaderLoginFailure": "Login Failure", + "LabelAllPlaysSentToPlayer": "Alle afspilninger vil blive sendt til den valgte afspiller.", + "MessageInvalidUser": "Ukendt brugernavn eller adgangskode. Pr\u00f8v igen.", + "HeaderLoginFailure": "Login fejl", "HeaderAllRecordings": "Alle optagelser", - "RecommendationBecauseYouLike": "Because you like {0}", - "RecommendationBecauseYouWatched": "Because you watched {0}", - "RecommendationDirectedBy": "Directed by {0}", - "RecommendationStarring": "Starring {0}", - "HeaderConfirmRecordingCancellation": "Confirm Recording Cancellation", - "MessageConfirmRecordingCancellation": "Are you sure you wish to cancel this recording?", - "MessageRecordingCancelled": "Recording cancelled.", - "HeaderConfirmSeriesCancellation": "Confirm Series Cancellation", - "MessageConfirmSeriesCancellation": "Are you sure you wish to cancel this series?", - "MessageSeriesCancelled": "Series cancelled.", - "HeaderConfirmRecordingDeletion": "Confirm Recording Deletion", - "MessageConfirmRecordingDeletion": "Are you sure you wish to delete this recording?", - "MessageRecordingDeleted": "Recording deleted.", - "ButonCancelRecording": "Cancel Recording", - "MessageRecordingSaved": "Recording saved.", + "RecommendationBecauseYouLike": "Fordi du kan lide {0}", + "RecommendationBecauseYouWatched": "Fordi du har set {0}", + "RecommendationDirectedBy": "Instrueret af {0}", + "RecommendationStarring": "Hovedrolleindehavere {0}", + "HeaderConfirmRecordingCancellation": "Bekr\u00e6ft annullering af optagelse", + "MessageConfirmRecordingCancellation": "Er du sikker p\u00e5 du \u00f8nsker at annullere denne optagelse?", + "MessageRecordingCancelled": "Optagelse annulleret.", + "HeaderConfirmSeriesCancellation": "Bekr\u00e6ft annullering af serie", + "MessageConfirmSeriesCancellation": "Er du sikker p\u00e5 du \u00f8nsker at annullere denne serie?", + "MessageSeriesCancelled": "Serie annulleret.", + "HeaderConfirmRecordingDeletion": "Bekr\u00e6ft sletning af optagelse", + "MessageConfirmRecordingDeletion": "Er du sikker p\u00e5 du \u00f8nsker at slette denne optagelse?", + "MessageRecordingDeleted": "Optagelse slettet.", + "ButonCancelRecording": "Annuller optagelse", + "MessageRecordingSaved": "Optagelse gemt.", "OptionSunday": "S\u00f8ndag", "OptionMonday": "Mandag", "OptionTuesday": "Tirsdag", @@ -181,332 +181,332 @@ "OptionFriday": "Fredag", "OptionSaturday": "L\u00f8rdag", "OptionEveryday": "Hver dag", - "OptionWeekend": "Weekends", - "OptionWeekday": "Weekdays", + "OptionWeekend": "Weekender", + "OptionWeekday": "Hverdage", "HeaderConfirmDeletion": "Bekr\u00e6ft sletning", - "MessageConfirmPathSubstitutionDeletion": "Are you sure you wish to delete this path substitution?", - "LiveTvUpdateAvailable": "(Update available)", - "LabelVersionUpToDate": "Up to date!", + "MessageConfirmPathSubstitutionDeletion": "Er du sikker p\u00e5 du \u00f8nsker at slette denne stisubstitution?", + "LiveTvUpdateAvailable": "(Opdatering tilg\u00e6ngelig)", + "LabelVersionUpToDate": "Opdateret!", "ButtonResetTuner": "Reset tuner", - "HeaderResetTuner": "Reset Tuner", - "MessageConfirmResetTuner": "Are you sure you wish to reset this tuner? Any active players or recordings will be abruptly stopped.", - "ButtonCancelSeries": "Cancel Series", - "HeaderSeriesRecordings": "Series Recordings", - "LabelAnytime": "Any time", - "StatusRecording": "Recording", - "StatusWatching": "Watching", - "StatusRecordingProgram": "Recording {0}", - "StatusWatchingProgram": "Watching {0}", - "HeaderSplitMedia": "Split Media Apart", - "MessageConfirmSplitMedia": "Are you sure you wish to split the media sources into separate items?", - "HeaderError": "Error", - "MessageChromecastConnectionError": "Your Chromecast receiver is unable to connect to your Emby Server. Please check their connections and try again.", - "MessagePleaseSelectOneItem": "Please select at least one item.", - "MessagePleaseSelectTwoItems": "Please select at least two items.", - "MessageTheFollowingItemsWillBeGrouped": "The following titles will be grouped into one item:", - "MessageConfirmItemGrouping": "Emby apps will automatically choose the optimal version to play based on device and network performance. Are you sure you wish to continue?", + "HeaderResetTuner": "Reset tuner", + "MessageConfirmResetTuner": "Er du sikker p\u00e5 du \u00f8nsker at resette denne tuner? Alle aktive afspilninger eller optagelser vil stoppe pludseligt.", + "ButtonCancelSeries": "Annuller serie", + "HeaderSeriesRecordings": "Serieoptagelser", + "LabelAnytime": "Alle tidspunkter", + "StatusRecording": "Optagelse", + "StatusWatching": "Ser", + "StatusRecordingProgram": "Optager {0}", + "StatusWatchingProgram": "Ser {0}", + "HeaderSplitMedia": "Opsplit medie", + "MessageConfirmSplitMedia": "Er du sikker p\u00e5 du \u00f8nsker at opsplitte mediekilderne til separate klilder?", + "HeaderError": "Fejl", + "MessageChromecastConnectionError": "Din Chromecast modtager kan ikke forbinde til din Emby Server. Tjek venligst deres forbindelse og pr\u00f8v igen.", + "MessagePleaseSelectOneItem": "V\u00e6lg venligst mindst \u00e9t element.", + "MessagePleaseSelectTwoItems": "V\u00e6lg venligst mindst to elementer.", + "MessageTheFollowingItemsWillBeGrouped": "F\u00f8lgende elementer vil blive grupperet til et element:", + "MessageConfirmItemGrouping": "Emby apps vil automatisk fors\u00f8ge at afspille den optimale version baseret p\u00e5 enheden og netv\u00e6rksydelse. Er du sikker p\u00e5 du \u00f8nsker at forts\u00e6tte?", "HeaderResume": "Fors\u00e6t", "HeaderMyViews": "Mine visninger", - "HeaderLibraryFolders": "Media Folders", + "HeaderLibraryFolders": "Mediemapper", "HeaderLatestMedia": "Seneste medier", "ButtonMoreItems": "Mere...", "ButtonMore": "Mere", - "HeaderFavoriteMovies": "Favorite Movies", - "HeaderFavoriteShows": "Favorite Shows", - "HeaderFavoriteEpisodes": "Favorite Episodes", - "HeaderFavoriteGames": "Favorite Games", - "HeaderRatingsDownloads": "Rating \/ Downloads", - "HeaderConfirmProfileDeletion": "Confirm Profile Deletion", - "MessageConfirmProfileDeletion": "Are you sure you wish to delete this profile?", - "HeaderSelectServerCachePath": "Select Server Cache Path", - "HeaderSelectTranscodingPath": "Select Transcoding Temporary Path", - "HeaderSelectImagesByNamePath": "Select Images By Name Path", - "HeaderSelectMetadataPath": "Select Metadata Path", - "HeaderSelectServerCachePathHelp": "Browse or enter the path to use for server cache files. The folder must be writeable.", - "HeaderSelectTranscodingPathHelp": "Browse or enter the path to use for transcoding temporary files. The folder must be writeable.", - "HeaderSelectImagesByNamePathHelp": "Browse or enter the path to your items by name folder. The folder must be writeable.", - "HeaderSelectMetadataPathHelp": "Browse or enter the path you'd like to store metadata within. The folder must be writeable.", - "HeaderSelectChannelDownloadPath": "Select Channel Download Path", - "HeaderSelectChannelDownloadPathHelp": "Browse or enter the path to use for storing channel cache files. The folder must be writeable.", - "OptionNewCollection": "New...", + "HeaderFavoriteMovies": "Favorit film", + "HeaderFavoriteShows": "Favorit serier", + "HeaderFavoriteEpisodes": "Favorit episoder", + "HeaderFavoriteGames": "Favorit spil", + "HeaderRatingsDownloads": "Bed\u00f8mmelser \/ Downloads", + "HeaderConfirmProfileDeletion": "Bekr\u00e6ft sletning af profil", + "MessageConfirmProfileDeletion": "Er du sikker p\u00e5 du \u00f8nsker at slette denne profil?", + "HeaderSelectServerCachePath": "V\u00e6lg \"Server Cache Path\"", + "HeaderSelectTranscodingPath": "V\u00e6lg \"Transcoding Temporary Path\"", + "HeaderSelectImagesByNamePath": "V\u00e6lg billeder efter navn-sti:", + "HeaderSelectMetadataPath": "V\u00e6lg Metadata Path", + "HeaderSelectServerCachePathHelp": "V\u00e6lg eller indtast stien som skal benyttes til serverens cache filer. Mappen m\u00e5 ikke v\u00e6re skrivebeskyttet.", + "HeaderSelectTranscodingPathHelp": "V\u00e6lg eller indtast stien som skal benyttes til midlertidige transkodningsfiler. Mappen m\u00e5 ikke v\u00e6re skrivebeskyttet.", + "HeaderSelectImagesByNamePathHelp": "V\u00e6lg eller indtast stien som f\u00f8rer til mappen med dine elmenter per navn. Mappen m\u00e5 ikke v\u00e6re skrivebeskyttet.", + "HeaderSelectMetadataPathHelp": "V\u00e6lg eller indtast stien for hvor du \u00f8nsker at gemme din metadata. Mappen m\u00e5 ikke v\u00e6re skrivebeskyttet.", + "HeaderSelectChannelDownloadPath": "V\u00e6lg sti for hentning af kanalindhold", + "HeaderSelectChannelDownloadPathHelp": "V\u00e6lg eller indtast stien for hvor du \u00f8nsker at gemme kanalindholds cache filer. Mappen m\u00e5 ikke v\u00e6re skrivebeskyttet.", + "OptionNewCollection": "Ny...", "ButtonAdd": "Tilf\u00f8j", "ButtonRemove": "Fjern", - "LabelChapterDownloaders": "Chapter downloaders:", - "LabelChapterDownloadersHelp": "Enable and rank your preferred chapter downloaders in order of priority. Lower priority downloaders will only be used to fill in missing information.", - "HeaderFavoriteAlbums": "Favorite Albums", - "HeaderLatestChannelMedia": "Latest Channel Items", - "ButtonOrganizeFile": "Organize File", - "ButtonDeleteFile": "Delete File", - "HeaderOrganizeFile": "Organize File", - "HeaderDeleteFile": "Delete File", - "StatusSkipped": "Skipped", - "StatusFailed": "Failed", - "StatusSuccess": "Success", - "MessageFileWillBeDeleted": "The following file will be deleted:", - "MessageSureYouWishToProceed": "Are you sure you wish to proceed?", - "MessageDuplicatesWillBeDeleted": "In addition the following dupliates will be deleted:", - "MessageFollowingFileWillBeMovedFrom": "The following file will be moved from:", - "MessageDestinationTo": "to:", - "HeaderSelectWatchFolder": "Select Watch Folder", - "HeaderSelectWatchFolderHelp": "Browse or enter the path to your watch folder. The folder must be writeable.", - "OrganizePatternResult": "Result: {0}", - "HeaderRestart": "Restart", - "HeaderShutdown": "Shutdown", - "MessageConfirmRestart": "Are you sure you wish to restart Emby Server?", - "MessageConfirmShutdown": "Are you sure you wish to shutdown Emby Server?", + "LabelChapterDownloaders": "Kapitel downloadere:", + "LabelChapterDownloadersHelp": "Aktiver og ranger dine fortrukne kapitel downloadere i en prioriteret r\u00e6kkef\u00f8lge. Lavt rangerende downloadere bliver kun benyttet til at udfylde manglende information.", + "HeaderFavoriteAlbums": "Favoritalbums", + "HeaderLatestChannelMedia": "Seneste kanalenheder", + "ButtonOrganizeFile": "Organiser fil", + "ButtonDeleteFile": "Slet fil", + "HeaderOrganizeFile": "Organiser fil", + "HeaderDeleteFile": "Slet fil", + "StatusSkipped": "Oversprunget", + "StatusFailed": "Fejlet", + "StatusSuccess": "Succes", + "MessageFileWillBeDeleted": "Den f\u00f8lgende fil vil blive slettet:", + "MessageSureYouWishToProceed": "\u00d8nsker du at forts\u00e6tte?", + "MessageDuplicatesWillBeDeleted": "Derudover vil f\u00f8lgende duplikater blive slettet:", + "MessageFollowingFileWillBeMovedFrom": "Den f\u00f8lgende fil vil blive flyttet fra:", + "MessageDestinationTo": "til:", + "HeaderSelectWatchFolder": "V\u00e6lg en Watch Folder", + "HeaderSelectWatchFolderHelp": "V\u00e6lg eller indtast stien til din \"watch folder\". Mappen m\u00e5 ikke v\u00e6re skrivebeskyttet.", + "OrganizePatternResult": "Resultat: {0}", + "HeaderRestart": "Genstart", + "HeaderShutdown": "Luk", + "MessageConfirmRestart": "Er du sikker p\u00e5 du \u00f8nsker at genstarte Emby?", + "MessageConfirmShutdown": "Er du sikker p\u00e5 du \u00f8nsker at lukke Emby?", "ButtonUpdateNow": "Opdater nu", - "ValueItemCount": "{0} item", - "ValueItemCountPlural": "{0} items", - "NewVersionOfSomethingAvailable": "A new version of {0} is available!", - "VersionXIsAvailableForDownload": "Version {0} is now available for download.", + "ValueItemCount": "{0} elment", + "ValueItemCountPlural": "{0} elementer", + "NewVersionOfSomethingAvailable": "En ny version af {0} er tilg\u00e6ngelig!", + "VersionXIsAvailableForDownload": "Version {0} kan nu downloades.", "LabelVersionNumber": "Version {0}", - "LabelPlayMethodTranscoding": "Transcoding", - "LabelPlayMethodDirectStream": "Direct Streaming", - "LabelPlayMethodDirectPlay": "Direct Playing", - "LabelAudioCodec": "Audio: {0}", + "LabelPlayMethodTranscoding": "Transkoding", + "LabelPlayMethodDirectStream": "Direkte streaming", + "LabelPlayMethodDirectPlay": "Direkte afspilning", + "LabelAudioCodec": "Lyd: {0}", "LabelVideoCodec": "Video: {0}", - "LabelLocalAccessUrl": "Local access: {0}", - "LabelRemoteAccessUrl": "Remote access: {0}", - "LabelRunningOnPort": "Running on http port {0}.", - "LabelRunningOnPorts": "Running on http port {0}, and https port {1}.", - "HeaderLatestFromChannel": "Latest from {0}", - "LabelUnknownLanaguage": "Unknown language", - "HeaderCurrentSubtitles": "Current Subtitles", - "MessageDownloadQueued": "The download has been queued.", - "MessageAreYouSureDeleteSubtitles": "Are you sure you wish to delete this subtitle file?", - "ButtonRemoteControl": "Remote Control", - "HeaderLatestTvRecordings": "Latest Recordings", + "LabelLocalAccessUrl": "Lokal adgang: {0}", + "LabelRemoteAccessUrl": "Fjernadgang: {0}", + "LabelRunningOnPort": "K\u00f8rer p\u00e5 http port {0}.", + "LabelRunningOnPorts": "K\u00f8rer p\u00e5 http port {0}, og https port {1}.", + "HeaderLatestFromChannel": "Seneste fra {0}", + "LabelUnknownLanaguage": "Ukendt sprog", + "HeaderCurrentSubtitles": "Nuv\u00e6rende undertekster", + "MessageDownloadQueued": "Downloadet er sat i k\u00f8.", + "MessageAreYouSureDeleteSubtitles": "Er du sikker p\u00e5 du \u00f8nsker at slette denne undertekstfil?", + "ButtonRemoteControl": "Fjernstyring", + "HeaderLatestTvRecordings": "Seneste optagelser", "ButtonOk": "Ok", "ButtonCancel": "Annuller", "ButtonRefresh": "Opdater", - "LabelCurrentPath": "Current path:", - "HeaderSelectMediaPath": "Select Media Path", - "HeaderSelectPath": "Select Path", - "ButtonNetwork": "Network", - "MessageDirectoryPickerInstruction": "Network paths can be entered manually in the event the Network button fails to locate your devices. For example, {0} or {1}.", + "LabelCurrentPath": "Nuv\u00e6rende sti:", + "HeaderSelectMediaPath": "V\u00e6lg mediesti", + "HeaderSelectPath": "V\u00e6lg sti", + "ButtonNetwork": "Netv\u00e6rk", + "MessageDirectoryPickerInstruction": "Netv\u00e6rksstier kan indtastes manuelt i tilf\u00e6lde af at netv\u00e6rksknappen ikke kan lokalisere dine enheder. Foreksempel, {0} eller {1}.", "HeaderMenu": "Menu", - "ButtonOpen": "Open", - "ButtonOpenInNewTab": "Open in new tab", - "ButtonShuffle": "Shuffle", - "ButtonInstantMix": "Instant mix", - "ButtonResume": "Resume", + "ButtonOpen": "\u00c5ben", + "ButtonOpenInNewTab": "\u00c5ben i ny fane", + "ButtonShuffle": "Bland", + "ButtonInstantMix": "Instant Mix", + "ButtonResume": "Genoptag", "HeaderScenes": "Scener", - "HeaderAudioTracks": "Audio Tracks", - "HeaderLibraries": "Libraries", - "HeaderSubtitles": "Subtitles", - "HeaderVideoQuality": "Video Quality", - "MessageErrorPlayingVideo": "There was an error playing the video.", - "MessageEnsureOpenTuner": "Please ensure there is an open tuner availalble.", + "HeaderAudioTracks": "Lydspor", + "HeaderLibraries": "Bibliotekter", + "HeaderSubtitles": "Undertekster", + "HeaderVideoQuality": "Videokvalitet", + "MessageErrorPlayingVideo": "Der opstod en fejl under afspilning af videoen.", + "MessageEnsureOpenTuner": "Sikre dig at en \u00e5ben tuner er tilg\u00e6ngelig.", "ButtonHome": "Hjem", - "ButtonDashboard": "Dashboard", - "ButtonReports": "Reports", + "ButtonDashboard": "Betjeningspanel", + "ButtonReports": "Rapporter", "ButtonMetadataManager": "Metadata Manager", - "HeaderTime": "Time", + "HeaderTime": "Tid", "HeaderName": "Navn", "HeaderAlbum": "Album", - "HeaderAlbumArtist": "Album Artist", + "HeaderAlbumArtist": "Album kunstner", "HeaderArtist": "Artist", - "LabelAddedOnDate": "Added {0}", + "LabelAddedOnDate": "Tilf\u00f8jet {0}", "ButtonStart": "Start", "HeaderChannels": "Kanaler", "HeaderMediaFolders": "Mediemapper", - "HeaderBlockItemsWithNoRating": "Block content with no rating information:", - "OptionBlockOthers": "Others", - "OptionBlockTvShows": "TV Shows", - "OptionBlockTrailers": "Trailers", - "OptionBlockMusic": "Music", - "OptionBlockMovies": "Movies", - "OptionBlockBooks": "Books", - "OptionBlockGames": "Games", - "OptionBlockLiveTvPrograms": "Live TV Programs", - "OptionBlockLiveTvChannels": "Live TV Channels", - "OptionBlockChannelContent": "Internet Channel Content", - "ButtonRevoke": "Revoke", - "MessageConfirmRevokeApiKey": "Are you sure you wish to revoke this api key? The application's connection to Emby Server will be abruptly terminated.", - "HeaderConfirmRevokeApiKey": "Revoke Api Key", - "ValueContainer": "Container: {0}", - "ValueAudioCodec": "Audio Codec: {0}", + "HeaderBlockItemsWithNoRating": "Bloker indhold uden bed\u00f8mmelser:", + "OptionBlockOthers": "Andre", + "OptionBlockTvShows": "TV serier", + "OptionBlockTrailers": "Trailere", + "OptionBlockMusic": "Musik", + "OptionBlockMovies": "Film", + "OptionBlockBooks": "B\u00f8ger", + "OptionBlockGames": "Spil", + "OptionBlockLiveTvPrograms": "Live TV-programmer", + "OptionBlockLiveTvChannels": "Live TV-kanaler", + "OptionBlockChannelContent": "Internet kanalindhold", + "ButtonRevoke": "Invalider", + "MessageConfirmRevokeApiKey": "Er du sikker p\u00e5 du \u00f8nsker at invalidere denne api n\u00f8gle? Applikationens forbindelse til Emby vil blive afbrudt \u00f8jeblikkeligt.", + "HeaderConfirmRevokeApiKey": "Invalider Api n\u00f8gle", + "ValueContainer": "Beholder: {0}", + "ValueAudioCodec": "Audio codec: {0}", "ValueVideoCodec": "Video Codec: {0}", "ValueCodec": "Codec: {0}", - "ValueConditions": "Conditions: {0}", - "LabelAll": "All", - "HeaderDeleteImage": "Delete Image", - "MessageFileNotFound": "File not found.", - "MessageFileReadError": "An error occurred reading this file.", - "ButtonNextPage": "Next Page", - "ButtonPreviousPage": "Previous Page", - "ButtonMoveLeft": "Move left", - "ButtonMoveRight": "Move right", - "ButtonBrowseOnlineImages": "Browse online images", - "HeaderDeleteItem": "Delete Item", - "ConfirmDeleteItem": "Deleting this item will delete it from both the file system and your media library. Are you sure you wish to continue?", - "MessagePleaseEnterNameOrId": "Please enter a name or an external Id.", - "MessageValueNotCorrect": "The value entered is not correct. Please try again.", - "MessageItemSaved": "Item saved.", - "MessagePleaseAcceptTermsOfServiceBeforeContinuing": "Please accept the terms of service before continuing.", + "ValueConditions": "Forhold: {0}", + "LabelAll": "Alle", + "HeaderDeleteImage": "Slet billede", + "MessageFileNotFound": "Fil blev ikke fundet.", + "MessageFileReadError": "Der opstod en fejl i fors\u00f8get p\u00e5 at l\u00e6se filen.", + "ButtonNextPage": "N\u00e6ste side", + "ButtonPreviousPage": "Forrige side", + "ButtonMoveLeft": "Flyt til venstre", + "ButtonMoveRight": "Flyt til h\u00f8jre", + "ButtonBrowseOnlineImages": "Gennemse online billeder", + "HeaderDeleteItem": "Slet element", + "ConfirmDeleteItem": "Hvis dette element slettes, fjernes det b\u00e5de fra dit filsystem samt din mediebibliotek. Er du sikker p\u00e5 du \u00f8nsker at forts\u00e6tte?", + "MessagePleaseEnterNameOrId": "Indtast venligst et navn eller eksternt Id.", + "MessageValueNotCorrect": "Det indtastede v\u00e6rdi er ikke korrekt. Pr\u00f8v igen.", + "MessageItemSaved": "Element gemt.", + "MessagePleaseAcceptTermsOfServiceBeforeContinuing": "Accepter venligst tjenestevilk\u00e5rene f\u00f8r du forts\u00e6tter.", "OptionEnded": "F\u00e6rdig", "OptionContinuing": "Fors\u00e6ttes", "OptionOff": "Off", "OptionOn": "On", "ButtonSettings": "Indstillinger", - "ButtonUninstall": "Uninstall", - "HeaderFields": "Fields", - "HeaderFieldsHelp": "Slide a field to 'off' to lock it and prevent it's data from being changed.", + "ButtonUninstall": "Afinstaller", + "HeaderFields": "Felter", + "HeaderFieldsHelp": "\u00c6ndre et felt til \"afbrudt\" for at l\u00e5se det og forhindre dets data i at blive \u00e6ndret.", "HeaderLiveTV": "Live TV", - "MissingLocalTrailer": "Missing local trailer.", - "MissingPrimaryImage": "Missing primary image.", - "MissingBackdropImage": "Missing backdrop image.", - "MissingLogoImage": "Missing logo image.", - "MissingEpisode": "Missing episode.", - "OptionScreenshots": "Screenshots", - "OptionBackdrops": "Backdrops", - "OptionImages": "Images", - "OptionKeywords": "Keywords", + "MissingLocalTrailer": "Mangler lokal trailer.", + "MissingPrimaryImage": "Mangler prim\u00e6rt billede", + "MissingBackdropImage": "Mangler baggrundsbillede.", + "MissingLogoImage": "Mangler logo.", + "MissingEpisode": "Mangler episode.", + "OptionScreenshots": "Sk\u00e6rmbilleder", + "OptionBackdrops": "Baggrunde", + "OptionImages": "Billeder", + "OptionKeywords": "N\u00f8gleord", "OptionTags": "Tags", - "OptionStudios": "Studios", - "OptionName": "Name", - "OptionOverview": "Overview", - "OptionGenres": "Genres", + "OptionStudios": "Studier", + "OptionName": "Navn", + "OptionOverview": "Oversigt", + "OptionGenres": "Genrer", "OptionParentalRating": "Aldersgr\u00e6nse", - "OptionPeople": "People", + "OptionPeople": "Personer", "OptionRuntime": "Varighed", - "OptionProductionLocations": "Production Locations", - "OptionBirthLocation": "Birth Location", - "LabelAllChannels": "All channels", - "LabelLiveProgram": "LIVE", - "LabelNewProgram": "NEW", - "LabelPremiereProgram": "PREMIERE", + "OptionProductionLocations": "Produktionslokationer", + "OptionBirthLocation": "F\u00f8dselssted", + "LabelAllChannels": "Alle kanaler", + "LabelLiveProgram": "DIREKTE", + "LabelNewProgram": "NY", + "LabelPremiereProgram": "PR\u00c6MIERE", "LabelHDProgram": "HD", - "HeaderChangeFolderType": "Change Content Type", - "HeaderChangeFolderTypeHelp": "To change the type, please remove and rebuild the folder with the new type.", - "HeaderAlert": "Alert", - "MessagePleaseRestart": "Please restart to finish updating.", + "HeaderChangeFolderType": "\u00c6ndre indholdstype", + "HeaderChangeFolderTypeHelp": "For at \u00e6ndre typen bedes du fjerne og gendanne mappen med den nye type.", + "HeaderAlert": "Advarsel", + "MessagePleaseRestart": "Genstart venligst for at afslutte opdateringen.", "ButtonRestart": "Genstart", - "MessagePleaseRefreshPage": "Please refresh this page to receive new updates from the server.", - "ButtonHide": "Hide", - "MessageSettingsSaved": "Settings saved.", - "ButtonSignOut": "Sign Out", - "ButtonMyProfile": "My Profile", - "ButtonMyPreferences": "My Preferences", - "MessageBrowserDoesNotSupportWebSockets": "This browser does not support web sockets. For a better experience, try a newer browser such as Chrome, Firefox, IE10+, Safari (iOS) or Opera.", - "LabelInstallingPackage": "Installing {0}", - "LabelPackageInstallCompleted": "{0} installation completed.", - "LabelPackageInstallFailed": "{0} installation failed.", - "LabelPackageInstallCancelled": "{0} installation cancelled.", + "MessagePleaseRefreshPage": "Genindl\u00e6s venligst denne side for at modtage nye opdateringer fra serveren.", + "ButtonHide": "Gem", + "MessageSettingsSaved": "Indstillinger er gemt.", + "ButtonSignOut": "Log af", + "ButtonMyProfile": "Min profil", + "ButtonMyPreferences": "Mine indstillinger", + "MessageBrowserDoesNotSupportWebSockets": "Denne browser underst\u00f8tter ikke \"web sockets\". For en bedre oplevelse benyt da en nyere browser s\u00e5 som Chrome, Firefox, IE10+, Safari (iOS) eller Opera.", + "LabelInstallingPackage": "Installerer {0}", + "LabelPackageInstallCompleted": "{0} installation udf\u00f8rt.", + "LabelPackageInstallFailed": "{0} installationen mislykkedes.", + "LabelPackageInstallCancelled": "{0} installation afbrudt.", "TabServer": "Server", "TabUsers": "Brugere", - "TabLibrary": "Library", + "TabLibrary": "Bibliotek", "TabMetadata": "Metadata", "TabDLNA": "DLNA", "TabLiveTV": "Live TV", - "TabAutoOrganize": "Auto-Organize", - "TabPlugins": "Plugins", + "TabAutoOrganize": "Organiser automatisk", + "TabPlugins": "Tilf\u00f8jelser", "TabAdvanced": "Avanceret", - "TabHelp": "Help", + "TabHelp": "Hj\u00e6lp", "TabScheduledTasks": "Planlagte opgaver", "ButtonFullscreen": "Fuldsk\u00e6rm", "ButtonAudioTracks": "Lydpor", "ButtonSubtitles": "Undertekster", "ButtonScenes": "Scener", - "ButtonQuality": "Quality", - "HeaderNotifications": "Notifications", - "HeaderSelectPlayer": "Select Player:", + "ButtonQuality": "Kvalitet", + "HeaderNotifications": "Notifikationer", + "HeaderSelectPlayer": "V\u00e6lg afspiller:", "ButtonSelect": "V\u00e6lg", "ButtonNew": "Ny", - "MessageInternetExplorerWebm": "For best results with Internet Explorer please install the WebM playback plugin.", - "HeaderVideoError": "Video Error", + "MessageInternetExplorerWebm": "For at opn\u00e5 de bedste resultater med Internet Explorer bedes du installere WebM afspilningstilf\u00f8jelsen.", + "HeaderVideoError": "Video fejl", "ButtonAddToPlaylist": "Tilf\u00f8j til afspilningsliste", - "HeaderAddToPlaylist": "Add to Playlist", + "HeaderAddToPlaylist": "Tilf\u00f8j til afspilningsliste", "LabelName": "Navn:", "ButtonSubmit": "Indsend", - "LabelSelectPlaylist": "Playlist:", - "OptionNewPlaylist": "New playlist...", + "LabelSelectPlaylist": "Afspilningsliste:", + "OptionNewPlaylist": "Ny afspilningsliste...", "MessageAddedToPlaylistSuccess": "Ok", "ButtonView": "Visning", - "ButtonViewSeriesRecording": "View series recording", - "ValueOriginalAirDate": "Original air date: {0}", - "ButtonRemoveFromPlaylist": "Remove from playlist", - "HeaderSpecials": "Specials", - "HeaderTrailers": "Trailers", - "HeaderAudio": "Audio", - "HeaderResolution": "Resolution", + "ButtonViewSeriesRecording": "Vis serieoptagelse", + "ValueOriginalAirDate": "Blev sendt f\u00f8rste gang: {0}", + "ButtonRemoveFromPlaylist": "Fjer fra afspilningsliste", + "HeaderSpecials": "S\u00e6rudsendelser", + "HeaderTrailers": "Trailere", + "HeaderAudio": "Lyd", + "HeaderResolution": "Opl\u00f8sning", "HeaderVideo": "Video", - "HeaderRuntime": "Runtime", - "HeaderCommunityRating": "Community rating", + "HeaderRuntime": "Varighed", + "HeaderCommunityRating": "F\u00e6llesskabsvurdering", "HeaderPasswordReset": "Nulstil adgangskode", - "HeaderParentalRating": "Parental rating", - "HeaderReleaseDate": "Release date", - "HeaderDateAdded": "Date added", - "HeaderSeries": "Series", - "HeaderSeason": "Season", - "HeaderSeasonNumber": "Season number", - "HeaderNetwork": "Network", - "HeaderYear": "Year", - "HeaderGameSystem": "Game system", - "HeaderPlayers": "Players", - "HeaderEmbeddedImage": "Embedded image", - "HeaderTrack": "Track", - "HeaderDisc": "Disc", + "HeaderParentalRating": "Aldersgr\u00e6nse", + "HeaderReleaseDate": "Udgivelsesdato", + "HeaderDateAdded": "Dato for tilf\u00f8jelse", + "HeaderSeries": "Serier", + "HeaderSeason": "S\u00e6son", + "HeaderSeasonNumber": "S\u00e6sonnummer", + "HeaderNetwork": "Netv\u00e6rk", + "HeaderYear": "\u00c5r", + "HeaderGameSystem": "Spilsystem", + "HeaderPlayers": "Afspillere", + "HeaderEmbeddedImage": "Indlejret billede", + "HeaderTrack": "Spor", + "HeaderDisc": "Disk", "OptionMovies": "Film", - "OptionCollections": "Collections", - "OptionSeries": "Series", - "OptionSeasons": "Seasons", + "OptionCollections": "Samlinger", + "OptionSeries": "Serier", + "OptionSeasons": "S\u00e6soner", "OptionEpisodes": "Episoder", - "OptionGames": "Games", - "OptionGameSystems": "Game systems", - "OptionMusicArtists": "Music artists", - "OptionMusicAlbums": "Music albums", - "OptionMusicVideos": "Music videos", - "OptionSongs": "Songs", - "OptionHomeVideos": "Home videos", - "OptionBooks": "Books", - "OptionAdultVideos": "Adult videos", - "ButtonUp": "Up", - "ButtonDown": "Down", - "LabelMetadataReaders": "Metadata readers:", - "LabelMetadataReadersHelp": "Rank your preferred local metadata sources in order of priority. The first file found will be read.", - "LabelMetadataDownloaders": "Metadata downloaders:", - "LabelMetadataDownloadersHelp": "Enable and rank your preferred metadata downloaders in order of priority. Lower priority downloaders will only be used to fill in missing information.", - "LabelMetadataSavers": "Metadata savers:", - "LabelMetadataSaversHelp": "Choose the file formats to save your metadata to.", - "LabelImageFetchers": "Image fetchers:", - "LabelImageFetchersHelp": "Enable and rank your preferred image fetchers in order of priority.", - "ButtonQueueAllFromHere": "Queue all from here", - "ButtonPlayAllFromHere": "Play all from here", + "OptionGames": "Spil", + "OptionGameSystems": "Spilsystemer", + "OptionMusicArtists": "Musikartister", + "OptionMusicAlbums": "Musikalbummer", + "OptionMusicVideos": "Musikvideoer", + "OptionSongs": "Sange", + "OptionHomeVideos": "Hjemmevideoer", + "OptionBooks": "B\u00f8ger", + "OptionAdultVideos": "Voksenfilm", + "ButtonUp": "Op", + "ButtonDown": "Ned", + "LabelMetadataReaders": "Metadata afl\u00e6sere:", + "LabelMetadataReadersHelp": "Ranger dine fortrukne lokale metadatakilder i prioriteret r\u00e6kkef\u00f8lge. Den f\u00f8rst fundne fil vil blive afl\u00e6st.", + "LabelMetadataDownloaders": "Metadata downloadere:", + "LabelMetadataDownloadersHelp": "Aktiver og ranger dine fortrukne metadata downloadere i en prioriteret r\u00e6kkef\u00f8lge. Lavt rangerende downloadere bliver kun benyttet til at udfylde manglende information.", + "LabelMetadataSavers": "Metadata-gemmer:", + "LabelMetadataSaversHelp": "V\u00e6lg de filformater du \u00f8nsker din metadata gemmes som.", + "LabelImageFetchers": "Billede-henter:", + "LabelImageFetchersHelp": "Aktiver og ranger dine fortrukne billede-hentere i en prioriteret r\u00e6kkef\u00f8lge.", + "ButtonQueueAllFromHere": "Set alt her i k\u00f8", + "ButtonPlayAllFromHere": "Afspil alt fra her", "LabelDynamicExternalId": "{0} Id:", - "HeaderIdentify": "Identify Item", + "HeaderIdentify": "Identificer genstand", "PersonTypePerson": "Person", - "LabelTitleDisplayOrder": "Title display order:", - "OptionSortName": "Sort name", + "LabelTitleDisplayOrder": "Titelvisningsorden:", + "OptionSortName": "Sorteringsnavn", "OptionReleaseDate": "Udgivelsesdato", "LabelSeasonNumber": "S\u00e6sonnummer", - "LabelDiscNumber": "Disc number", - "LabelParentNumber": "Parent number", + "LabelDiscNumber": "Disk nummer", + "LabelParentNumber": "Parent nummer", "LabelEpisodeNumber": "Episodenummer", - "LabelTrackNumber": "Track number:", - "LabelNumber": "Number:", + "LabelTrackNumber": "Spor nummer:", + "LabelNumber": "Nummer:", "LabelReleaseDate": "Udgivelsesdato:", "LabelEndDate": "Slutdato:", "LabelYear": "\u00c5r:", - "LabelDateOfBirth": "Date of birth:", - "LabelBirthYear": "Birth year:", - "LabelBirthDate": "Birth date:", - "LabelDeathDate": "Death date:", - "HeaderRemoveMediaLocation": "Remove Media Location", - "MessageConfirmRemoveMediaLocation": "Are you sure you wish to remove this location?", - "HeaderRenameMediaFolder": "Rename Media Folder", - "LabelNewName": "New name:", - "HeaderAddMediaFolder": "Add Media Folder", - "HeaderAddMediaFolderHelp": "Name (Movies, Music, TV, etc):", - "HeaderRemoveMediaFolder": "Remove Media Folder", - "MessageTheFollowingLocationWillBeRemovedFromLibrary": "The following media locations will be removed from your library:", - "MessageAreYouSureYouWishToRemoveMediaFolder": "Are you sure you wish to remove this media folder?", - "ButtonRename": "Rename", - "ButtonChangeType": "Change type", - "HeaderMediaLocations": "Media Locations", - "LabelContentTypeValue": "Content type: {0}", - "LabelPathSubstitutionHelp": "Optional: Path substitution can map server paths to network shares that clients can access for direct playback.", - "FolderTypeUnset": "Unset (mixed content)", + "LabelDateOfBirth": "F\u00f8dselsdato:", + "LabelBirthYear": "F\u00f8dsels\u00e5r:", + "LabelBirthDate": "F\u00f8dselsdato:", + "LabelDeathDate": "D\u00f8dsdato:", + "HeaderRemoveMediaLocation": "Fjern medielokalisation", + "MessageConfirmRemoveMediaLocation": "Er du sikker p\u00e5 du \u00f8nsker at fjerne denne lokalisation?", + "HeaderRenameMediaFolder": "Omd\u00f8b mediemappe", + "LabelNewName": "Nyt navn:", + "HeaderAddMediaFolder": "Tilf\u00f8j mediemappe", + "HeaderAddMediaFolderHelp": "Navn (Film, Musik, TV, osv.):", + "HeaderRemoveMediaFolder": "Fjern mediemappe", + "MessageTheFollowingLocationWillBeRemovedFromLibrary": "F\u00f8lgende medielokationer vil blive fjerne fra dit bibliotek:", + "MessageAreYouSureYouWishToRemoveMediaFolder": "Er du sikker p\u00e5 du \u00f8nsker at fjerne denne mediemappe?", + "ButtonRename": "Omd\u00f8b", + "ButtonChangeType": "\u00c6ndre type", + "HeaderMediaLocations": "Medielokationer", + "LabelContentTypeValue": "Indholdstype: {0}", + "LabelPathSubstitutionHelp": "Valgfri: Stisubstitution kan sammenk\u00e6de serverstier til netv\u00e6rksstier som klienter derved kan tilg\u00e5 for direkte afspilning.", + "FolderTypeUnset": "Ikke valgt (blandet indhold)", "FolderTypeMovies": "FIlm", "FolderTypeMusic": "Musik", "FolderTypeAdultVideos": "Voksenfilm", @@ -524,247 +524,247 @@ "TabAlbums": "Albums", "TabSongs": "Sange", "TabMusicVideos": "Musikvideoer", - "BirthPlaceValue": "Birth place: {0}", - "DeathDateValue": "Died: {0}", - "BirthDateValue": "Born: {0}", - "HeaderLatestReviews": "Latest Reviews", - "HeaderPluginInstallation": "Plugin Installation", - "MessageAlreadyInstalled": "This version is already installed.", - "ValueReviewCount": "{0} Reviews", - "MessageYouHaveVersionInstalled": "You currently have version {0} installed.", - "MessageTrialExpired": "The trial period for this feature has expired", - "MessageTrialWillExpireIn": "The trial period for this feature will expire in {0} day(s)", - "MessageInstallPluginFromApp": "This plugin must be installed from with in the app you intend to use it in.", - "ValuePriceUSD": "Price: {0} (USD)", - "MessageFeatureIncludedWithSupporter": "You are registered for this feature, and will be able to continue using it with an active supporter membership.", - "MessageChangeRecurringPlanConfirm": "After completing this transaction you will need to cancel your previous recurring donation from within your PayPal account. Thank you for supporting Emby.", - "MessageSupporterMembershipExpiredOn": "Your supporter membership expired on {0}.", - "MessageYouHaveALifetimeMembership": "You have a lifetime supporter membership. You can provide additional donations on a one-time or recurring basis using the options below. Thank you for supporting Emby.", - "MessageYouHaveAnActiveRecurringMembership": "You have an active {0} membership. You can upgrade your plan using the options below.", + "BirthPlaceValue": "F\u00f8dselssted: {0}", + "DeathDateValue": "D\u00f8dsdato: {0}", + "BirthDateValue": "F\u00f8dt: {0}", + "HeaderLatestReviews": "Seneste anmeldeser", + "HeaderPluginInstallation": "Plugin installation", + "MessageAlreadyInstalled": "Denne version er allerede installeret.", + "ValueReviewCount": "{0} Anmeldelser", + "MessageYouHaveVersionInstalled": "Du har version {0} installeret.", + "MessageTrialExpired": "Pr\u00f8veperioden for denne funktion er udl\u00f8bet", + "MessageTrialWillExpireIn": "Pr\u00f8veperioden for denne funktion udl\u00f8ber om {0} dag(e)", + "MessageInstallPluginFromApp": "Dette plugin skal v\u00e6re installeret inde i den app du \u00f8nsker at benytte det fra.", + "ValuePriceUSD": "Pris: {0} (USD)", + "MessageFeatureIncludedWithSupporter": "Du er registreret til at benytte denne funktion, og kan blive ved med at benytte den under foruds\u00e6tning af et aktivt supporter medlemsskab.", + "MessageChangeRecurringPlanConfirm": "Efter denne transaktion er udf\u00f8rt skal du afmelde din tidligere l\u00f8bende donation inde fra din PayPal konto. Tak fordi du st\u00f8tter Emby.", + "MessageSupporterMembershipExpiredOn": "Dit supporter medlemskab udl\u00f8b den {0}.", + "MessageYouHaveALifetimeMembership": "Du har et livstidsmedlemskab. Du kan give yderligere donationer via en engangsydelse eller p\u00e5 l\u00f8bende basis ved at benytte mulighederne nedenfor. Tak fordi du st\u00f8tter Emby.", + "MessageYouHaveAnActiveRecurringMembership": "Du har et aktivt {0} medlemsskab. Du kan opgradere dette via mulighederne nedenfor.", "ButtonDelete": "Slet", - "HeaderEmbyAccountAdded": "Emby Account Added", - "MessageEmbyAccountAdded": "The Emby account has been added to this user.", - "MessagePendingEmbyAccountAdded": "The Emby account has been added to this user. An email will be sent to the owner of the account. The invitation will need to be confirmed by clicking a link within the email.", - "HeaderEmbyAccountRemoved": "Emby Account Removed", - "MessageEmbyAccontRemoved": "The Emby account has been removed from this user.", - "TooltipLinkedToEmbyConnect": "Linked to Emby Connect", - "HeaderUnrated": "Unrated", - "ValueDiscNumber": "Disc {0}", - "HeaderUnknownDate": "Unknown Date", - "HeaderUnknownYear": "Unknown Year", + "HeaderEmbyAccountAdded": "Emby konto tilf\u00f8jet", + "MessageEmbyAccountAdded": "Emby kontoen er blevet tilf\u00f8jet til denne bruger.", + "MessagePendingEmbyAccountAdded": "Emby kontoen er blevet tilf\u00f8jet denne bruger. En email sendes til ejeren af kontoen. Invitationen skal bekr\u00e6ftes ved at klikke p\u00e5 linket i emailen.", + "HeaderEmbyAccountRemoved": "Emby konto fjernet", + "MessageEmbyAccontRemoved": "Emby kontoen er blevet fjernet fra denne bruger.", + "TooltipLinkedToEmbyConnect": "Koblet til Emby Connect", + "HeaderUnrated": "Ingen bed\u00f8mmelse", + "ValueDiscNumber": "Disk {0}", + "HeaderUnknownDate": "Ukendt dato", + "HeaderUnknownYear": "Ukendt \u00e5r", "ValueMinutes": "{0} min", - "ButtonPlayExternalPlayer": "Play with external player", - "HeaderSelectExternalPlayer": "Select External Player", - "HeaderExternalPlayerPlayback": "External Player Playback", - "ButtonImDone": "I'm Done", - "OptionWatched": "Watched", - "OptionUnwatched": "Unwatched", - "ExternalPlayerPlaystateOptionsHelp": "Specify how you would like to resume playing this video next time.", - "LabelMarkAs": "Mark as:", - "OptionInProgress": "In-Progress", - "LabelResumePoint": "Resume point:", - "ValueOneMovie": "1 movie", - "ValueMovieCount": "{0} movies", + "ButtonPlayExternalPlayer": "Afspil med ekstern afspiller", + "HeaderSelectExternalPlayer": "V\u00e6lg ekstern afspiller", + "HeaderExternalPlayerPlayback": "Ekstern afspiller afspilning", + "ButtonImDone": "Jeg er f\u00e6rdig", + "OptionWatched": "Set", + "OptionUnwatched": "Ikke set", + "ExternalPlayerPlaystateOptionsHelp": "Specificer hvordan du gerne vil genoptage afspilningen af denne video n\u00e6ste gang.", + "LabelMarkAs": "Marker som:", + "OptionInProgress": "I gang", + "LabelResumePoint": "Genoptagelsespunkt:", + "ValueOneMovie": "1 film", + "ValueMovieCount": "{0} film", "ValueOneTrailer": "1 trailer", - "ValueTrailerCount": "{0} trailers", - "ValueOneSeries": "1 series", - "ValueSeriesCount": "{0} series", + "ValueTrailerCount": "{0} trailere", + "ValueOneSeries": "1 serie", + "ValueSeriesCount": "{0} serier", "ValueOneEpisode": "1 episode", - "ValueEpisodeCount": "{0} episodes", - "ValueOneGame": "1 game", - "ValueGameCount": "{0} games", + "ValueEpisodeCount": "{0} episoder", + "ValueOneGame": "1 spil", + "ValueGameCount": "{0} spil", "ValueOneAlbum": "1 album", - "ValueAlbumCount": "{0} albums", - "ValueOneSong": "1 song", - "ValueSongCount": "{0} songs", - "ValueOneMusicVideo": "1 music video", - "ValueMusicVideoCount": "{0} music videos", + "ValueAlbumCount": "{0} album", + "ValueOneSong": "1 sang", + "ValueSongCount": "{0} sange", + "ValueOneMusicVideo": "1 musikvideo", + "ValueMusicVideoCount": "{0} musikvideoer", "HeaderOffline": "Offline", - "HeaderUnaired": "Unaired", - "HeaderMissing": "Missing", - "ButtonWebsite": "Website", - "TooltipFavorite": "Favorite", + "HeaderUnaired": "Ikke sendt", + "HeaderMissing": "Mangler", + "ButtonWebsite": "Hjemmeside", + "TooltipFavorite": "Favorit", "TooltipLike": "Like", "TooltipDislike": "Dislike", - "TooltipPlayed": "Played", - "ValueSeriesYearToPresent": "{0}-Present", - "ValueAwards": "Awards: {0}", + "TooltipPlayed": "Afspillet", + "ValueSeriesYearToPresent": "{0}-Nu", + "ValueAwards": "Priser: {0}", "ValueBudget": "Budget: {0}", - "ValueRevenue": "Revenue: {0}", - "ValuePremiered": "Premiered {0}", - "ValuePremieres": "Premieres {0}", - "ValueStudio": "Studio: {0}", - "ValueStudios": "Studios: {0}", + "ValueRevenue": "Indtjening: {0}", + "ValuePremiered": "Pr\u00e6miere {0}", + "ValuePremieres": "Pr\u00e6miere {0}", + "ValueStudio": "Studie: {0}", + "ValueStudios": "Studier: {0}", "ValueStatus": "Status: {0}", "ValueSpecialEpisodeName": "Special - {0}", - "LabelLimit": "Limit:", + "LabelLimit": "Gr\u00e6nse:", "ValueLinks": "Links: {0}", "HeaderPeople": "Mennesker", - "HeaderCastAndCrew": "Cast & Crew", - "ValueArtist": "Artist: {0}", - "ValueArtists": "Artists: {0}", + "HeaderCastAndCrew": "Medvirkende", + "ValueArtist": "Kunstner: {0}", + "ValueArtists": "Kunstnere: {0}", "HeaderTags": "Tags", - "MediaInfoCameraMake": "Camera make", - "MediaInfoCameraModel": "Camera model", - "MediaInfoAltitude": "Altitude", - "MediaInfoAperture": "Aperture", - "MediaInfoExposureTime": "Exposure time", - "MediaInfoFocalLength": "Focal length", - "MediaInfoOrientation": "Orientation", - "MediaInfoIsoSpeedRating": "Iso speed rating", - "MediaInfoLatitude": "Latitude", - "MediaInfoLongitude": "Longitude", - "MediaInfoShutterSpeed": "Shutter speed", + "MediaInfoCameraMake": "Kameram\u00e6rke", + "MediaInfoCameraModel": "Kameramodel", + "MediaInfoAltitude": "H\u00f8jde", + "MediaInfoAperture": "Bl\u00e6nde", + "MediaInfoExposureTime": "Eksponering", + "MediaInfoFocalLength": "Br\u00e6ndvidde", + "MediaInfoOrientation": "Orientering", + "MediaInfoIsoSpeedRating": "Iso hastigheds rating", + "MediaInfoLatitude": "Breddegrad", + "MediaInfoLongitude": "H\u00f8jdegrad", + "MediaInfoShutterSpeed": "Lukkehastighed", "MediaInfoSoftware": "Software", - "HeaderIfYouLikeCheckTheseOut": "If you like {0}, check these out...", + "HeaderIfYouLikeCheckTheseOut": "Hvis du kan lide {0}, s\u00e5 tjek disse...", "HeaderPlotKeywords": "Plot n\u00f8gleord", - "HeaderMovies": "Movies", + "HeaderMovies": "Film", "HeaderAlbums": "Albums", - "HeaderGames": "Games", - "HeaderBooks": "Books", + "HeaderGames": "Spil", + "HeaderBooks": "B\u00f8ger", "HeaderEpisodes": "Afsnit", - "HeaderSeasons": "Seasons", - "HeaderTracks": "Tracks", - "HeaderItems": "Items", - "HeaderOtherItems": "Other Items", - "ButtonFullReview": "Full review", - "ValueAsRole": "as {0}", - "ValueGuestStar": "Guest star", - "MediaInfoSize": "Size", - "MediaInfoPath": "Path", + "HeaderSeasons": "S\u00e6soner", + "HeaderTracks": "Spor", + "HeaderItems": "Element", + "HeaderOtherItems": "Andre elementer", + "ButtonFullReview": "Fuld anmeldelse", + "ValueAsRole": "som {0}", + "ValueGuestStar": "G\u00e6stestjerne", + "MediaInfoSize": "St\u00f8rrelse", + "MediaInfoPath": "Sti", "MediaInfoFormat": "Format", - "MediaInfoContainer": "Container", - "MediaInfoDefault": "Default", - "MediaInfoForced": "Forced", - "MediaInfoExternal": "External", - "MediaInfoTimestamp": "Timestamp", - "MediaInfoPixelFormat": "Pixel format", - "MediaInfoBitDepth": "Bit depth", + "MediaInfoContainer": "Beholder", + "MediaInfoDefault": "Standard", + "MediaInfoForced": "Tvungen", + "MediaInfoExternal": "Ekstern", + "MediaInfoTimestamp": "Tidsstempel", + "MediaInfoPixelFormat": "Pixelformat", + "MediaInfoBitDepth": "Bit dybde", "MediaInfoSampleRate": "Sample rate", "MediaInfoBitrate": "Bitrate", - "MediaInfoChannels": "Channels", + "MediaInfoChannels": "Kanaler", "MediaInfoLayout": "Layout", - "MediaInfoLanguage": "Language", + "MediaInfoLanguage": "Sprog", "MediaInfoCodec": "Codec", - "MediaInfoProfile": "Profile", - "MediaInfoLevel": "Level", - "MediaInfoAspectRatio": "Aspect ratio", - "MediaInfoResolution": "Resolution", - "MediaInfoAnamorphic": "Anamorphic", + "MediaInfoProfile": "Profil", + "MediaInfoLevel": "Niveau", + "MediaInfoAspectRatio": "Formatforhold", + "MediaInfoResolution": "Opl\u00f8sning", + "MediaInfoAnamorphic": "Anamorfisk", "MediaInfoInterlaced": "Interlaced", "MediaInfoFramerate": "Framerate", - "MediaInfoStreamTypeAudio": "Audio", + "MediaInfoStreamTypeAudio": "Lyd", "MediaInfoStreamTypeData": "Data", "MediaInfoStreamTypeVideo": "Video", - "MediaInfoStreamTypeSubtitle": "Subtitle", - "MediaInfoStreamTypeEmbeddedImage": "Embedded Image", + "MediaInfoStreamTypeSubtitle": "Undertekster", + "MediaInfoStreamTypeEmbeddedImage": "Indlejret billede", "MediaInfoRefFrames": "Ref frames", "TabPlayback": "Afspilning", "TabNotifications": "Underretninger", - "TabExpert": "Expert", - "HeaderSelectCustomIntrosPath": "Select Custom Intros Path", - "HeaderRateAndReview": "Rate and Review", - "HeaderThankYou": "Thank You", - "MessageThankYouForYourReview": "Thank you for your review.", - "LabelYourRating": "Your rating:", - "LabelFullReview": "Full review:", - "LabelShortRatingDescription": "Short rating summary:", - "OptionIRecommendThisItem": "I recommend this item", - "WebClientTourContent": "View your recently added media, next episodes, and more. The green circles indicate how many unplayed items you have.", - "WebClientTourMovies": "Play movies, trailers and more from any device with a web browser", - "WebClientTourMouseOver": "Hold the mouse over any poster for quick access to important information", - "WebClientTourTapHold": "Tap and hold or right click any poster for a context menu", - "WebClientTourMetadataManager": "Click edit to open the metadata manager", - "WebClientTourPlaylists": "Easily create playlists and instant mixes, and play them on any device", - "WebClientTourCollections": "Create movie collections to group box sets together", - "WebClientTourUserPreferences1": "User preferences allow you to customize the way your library is presented in all of your Emby apps", - "WebClientTourUserPreferences2": "Configure your audio and subtitle language settings once, for every Emby app", - "WebClientTourUserPreferences3": "Design the web client home page to your liking", - "WebClientTourUserPreferences4": "Configure backdrops, theme songs and external players", - "WebClientTourMobile1": "The web client works great on smartphones and tablets...", - "WebClientTourMobile2": "and easily controls other devices and Emby apps", - "WebClientTourMySync": "Sync your personal media to your devices for offline viewing.", - "MessageEnjoyYourStay": "Enjoy your stay", - "DashboardTourDashboard": "The server dashboard allows you to monitor your server and your users. You'll always know who is doing what and where they are.", - "DashboardTourHelp": "In-app help provides easy buttons to open wiki pages relating to the on-screen content.", - "DashboardTourUsers": "Easily create user accounts for your friends and family, each with their own permissions, library access, parental controls and more.", - "DashboardTourCinemaMode": "Cinema mode brings the theater experience straight to your living room with the ability to play trailers and custom intros before the main feature.", - "DashboardTourChapters": "Enable chapter image generation for your videos for a more pleasing presentation while viewing.", - "DashboardTourSubtitles": "Automatically download subtitles for your videos in any language.", - "DashboardTourPlugins": "Install plugins such as internet video channels, live tv, metadata scanners, and more.", - "DashboardTourNotifications": "Automatically send notifications of server events to your mobile device, email and more.", - "DashboardTourScheduledTasks": "Easily manage long running operations with scheduled tasks. Decide when they run, and how often.", - "DashboardTourMobile": "The Emby Server dashboard works great on smartphones and tablets. Manage your server from the palm of your hand anytime, anywhere.", - "DashboardTourSync": "Sync your personal media to your devices for offline viewing.", - "MessageRefreshQueued": "Refresh queued", + "TabExpert": "Ekspert", + "HeaderSelectCustomIntrosPath": "V\u00e6lg sti til brugerdefinerede introduktioner", + "HeaderRateAndReview": "Bed\u00f8m og anmeld", + "HeaderThankYou": "Tak", + "MessageThankYouForYourReview": "Tak for din anmeldelse", + "LabelYourRating": "Din bed\u00f8mmelse:", + "LabelFullReview": "Fuld anmeldelse:", + "LabelShortRatingDescription": "Kort bed\u00f8mmelsesresum\u00e9:", + "OptionIRecommendThisItem": "Jeg anbefaler dette", + "WebClientTourContent": "Se dit seneste tilf\u00f8jet media, kommende episoder samt mere. Den gr\u00f8nne cirkel indikerer hvor mange uafspillet elementer du har.", + "WebClientTourMovies": "Afspil film, trailere samt andet fra hvilken som helst enhed med en browser", + "WebClientTourMouseOver": "Hold musen over enhver plakat for hurtig adgang til vigtig information", + "WebClientTourTapHold": "Tryk og hold eller h\u00f8jreklik p\u00e5 enhver plakat for at \u00e5bne en menu for det valgte element", + "WebClientTourMetadataManager": "Klik p\u00e5 rediger for at \u00e5bne metadata manageren", + "WebClientTourPlaylists": "Opret afspilningslister og instant mixes, og afspil dem p\u00e5 enhver enhed", + "WebClientTourCollections": "Opret filmsamlinger s\u00e5 film kan grupperes sammen", + "WebClientTourUserPreferences1": "Brugerindstillinger g\u00f8r det muligt for dig at skr\u00e6ddersy m\u00e5den dit bibliotek pr\u00e6senteres i alle dine Emby apps", + "WebClientTourUserPreferences2": "Konfigurer sproget p\u00e5 dine lyd og undertekstindstillinger \u00e9n gang for alle Emby apps", + "WebClientTourUserPreferences3": "Design webklient hjemmesiden til din egen smag", + "WebClientTourUserPreferences4": "V\u00e6lg baggrunde, temasange og eksterne afspillere", + "WebClientTourMobile1": "Webklienten virker perfekt p\u00e5 smartphones og tablets...", + "WebClientTourMobile2": "og styr let andre enheder og Emby apps", + "WebClientTourMySync": "Synkroniser dine personlige mediefiler til dine enheder s\u00e5 det kan ses offline.", + "MessageEnjoyYourStay": "Nyd dit bes\u00f8g", + "DashboardTourDashboard": "Betjeningspanelet g\u00f8r det muligt at monitorere din server og dine brugere. Du vil altid v\u00e6re i stand til at vide hvem der g\u00f8r hvad samt hvor de er.", + "DashboardTourHelp": "Hj\u00e6lp inde i app'en s\u00f8rger for knapper der let \u00e5bner de wiki-sider der er relateret til hvad der er p\u00e5 din sk\u00e6rm i det \u00f8jeblik.", + "DashboardTourUsers": "Opret let brugerkonti til dine venner og familie, hver med deres egne rettigheder, adgang til biblioteket, for\u00e6ldre-indstillinger samt meget mere.", + "DashboardTourCinemaMode": "Biograftilstand giver dig biografoplevelsen direkte ind i din stue, med muligheden for at vise trailere og brugerdefinerede introduktioner f\u00f8r hovedfilmen.", + "DashboardTourChapters": "Aktiver kapitelbillede-oprettelse for dine videoer for en mere behagelig pr\u00e6sentation mens du afspiller.", + "DashboardTourSubtitles": "Download automatisk undertekster til dine videoer in ethvert sprog.", + "DashboardTourPlugins": "Installer tilf\u00f8jelser s\u00e5 som internet videokanaler, live tv, metadata skannere samt meget mere.", + "DashboardTourNotifications": "Send automatisk notifikationer vedr\u00f8rende serverbegivenheder til dine mobile enheder, din email samt andre tjenester.", + "DashboardTourScheduledTasks": "Administrer let processer der l\u00f8ber over l\u00e6ngere tid via planlagte opgaver. Bestem hvorn\u00e5r de udf\u00f8res samt hvor ofte.", + "DashboardTourMobile": "Emby betjeningspanelet virker uden problemer p\u00e5 b\u00e5de smartphones og tablets. Kontrol over din server er altid ved dine fingrespidser hvor som helst, n\u00e5r som helst.", + "DashboardTourSync": "Synkroniser dine personlige mediefiler til dine enheder s\u00e5 det kan ses offline.", + "MessageRefreshQueued": "Opdatering sat i k\u00f8", "TabDevices": "Enheder", - "TabExtras": "Extras", - "DeviceLastUsedByUserName": "Last used by {0}", - "HeaderDeleteDevice": "Delete Device", - "DeleteDeviceConfirmation": "Are you sure you wish to delete this device? It will reappear the next time a user signs in with it.", - "LabelEnableCameraUploadFor": "Enable camera upload for:", - "HeaderSelectUploadPath": "Select Upload Path", - "LabelEnableCameraUploadForHelp": "Uploads will occur automatically in the background when signed into Emby.", - "ErrorMessageStartHourGreaterThanEnd": "End time must be greater than the start time.", - "ButtonLibraryAccess": "Library access", - "ButtonParentalControl": "Parental control", - "HeaderInvitationSent": "Invitation Sent", - "MessageInvitationSentToUser": "An email has been sent to {0}, inviting them to accept your sharing invitation.", - "MessageInvitationSentToNewUser": "An email has been sent to {0} inviting them to sign up with Emby.", - "HeaderConnectionFailure": "Connection Failure", - "MessageUnableToConnectToServer": "We're unable to connect to the selected server right now. Please ensure it is running and try again.", - "ButtonSelectServer": "Select server", - "MessagePluginConfigurationRequiresLocalAccess": "To configure this plugin please sign in to your local server directly.", - "MessageLoggedOutParentalControl": "Access is currently restricted. Please try again later.", - "DefaultErrorMessage": "There was an error processing the request. Please try again later.", - "ButtonAccept": "Accept", - "ButtonReject": "Reject", + "TabExtras": "Ekstra", + "DeviceLastUsedByUserName": "Sidst brugt af {0}", + "HeaderDeleteDevice": "Slet enhed", + "DeleteDeviceConfirmation": "Er du sikker p\u00e5 du \u00f8nsker at slette denne enhed? Den vil dukke op igen n\u00e6ste gang en bruger logger ind med den.", + "LabelEnableCameraUploadFor": "Aktiver kamera upload for:", + "HeaderSelectUploadPath": "V\u00e6lg upload sti", + "LabelEnableCameraUploadForHelp": "Uploads sker automatisk i baggrunden n\u00e5r du er logget p\u00e5 Emby", + "ErrorMessageStartHourGreaterThanEnd": "Slut tid skal v\u00e6re st\u00f8rre end start tid.", + "ButtonLibraryAccess": "Biblioteksadgang", + "ButtonParentalControl": "For\u00e6ldrekontrol", + "HeaderInvitationSent": "Invitation sendt", + "MessageInvitationSentToUser": "En email er blevet sendt til {0}, hvori de er blevet anmodet om at acceptere din invitation.", + "MessageInvitationSentToNewUser": "En email er blevet sendt til {0} med en invitation til at oprette sig hos Emby.", + "HeaderConnectionFailure": "Forbindelsesfejl", + "MessageUnableToConnectToServer": "Vi kan ikke forbinde til den valgte server p\u00e5 nuv\u00e6rende tidspunkt. Sikrer dig venligst at serveren k\u00f8rer og pr\u00f8v igen.", + "ButtonSelectServer": "V\u00e6lg server", + "MessagePluginConfigurationRequiresLocalAccess": "For at konfigurerer dette plugin log da venligst direkte ind p\u00e5 din lokale server.", + "MessageLoggedOutParentalControl": "Adgang er begr\u00e6nset p\u00e5 nuv\u00e6rende tidspunkt. Pr\u00f8v igen senere.", + "DefaultErrorMessage": "Det opstod en fejl ved behandlingen af foresp\u00f8rgslen. Pr\u00f8v igen senere.", + "ButtonAccept": "Accepter", + "ButtonReject": "Afvis", "HeaderForgotPassword": "Glemt adgangskode", - "MessageContactAdminToResetPassword": "Please contact your system administrator to reset your password.", - "MessageForgotPasswordInNetworkRequired": "Please try again within your home network to initiate the password reset process.", - "MessageForgotPasswordFileCreated": "The following file has been created on your server and contains instructions on how to proceed:", - "MessageForgotPasswordFileExpiration": "The reset pin will expire at {0}.", - "MessageInvalidForgotPasswordPin": "An invalid or expired pin was entered. Please try again.", - "MessagePasswordResetForUsers": "Passwords have been removed for the following users:", - "HeaderInviteGuest": "Invite Guest", - "ButtonLinkMyEmbyAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Emby account to this server.", + "MessageContactAdminToResetPassword": "Kontakt venligst din systemadministrator for at nulstille din adgangskode.", + "MessageForgotPasswordInNetworkRequired": "Pr\u00f8v igen inde i dit hjemmenetv\u00e6rk for at igangs\u00e6tte nulstilling af din adgangskode.", + "MessageForgotPasswordFileCreated": "Den f\u00f8lgende fil er blevet oprettet p\u00e5 din server og indeholder instruktioner vedr\u00f8rende hvordan du skal forts\u00e6tte:", + "MessageForgotPasswordFileExpiration": "Nulstillings pinkoden udl\u00f8ber {0}.", + "MessageInvalidForgotPasswordPin": "En ugyldig eller udl\u00f8bet pinkode blev indtastet. Pr\u00f8v igen.", + "MessagePasswordResetForUsers": "Adgangskoder er blevet fjernet fra f\u00f8lgende brugere:", + "HeaderInviteGuest": "Inviter g\u00e6st", + "ButtonLinkMyEmbyAccount": "Link min konto nu", + "MessageConnectAccountRequiredToInviteGuest": "For at invitere g\u00e6ster skal du f\u00f8rst k\u00e6de din Emby konto til denne server.", "ButtonSync": "Sync", - "SyncMedia": "Sync Media", - "HeaderCancelSyncJob": "Cancel Sync", - "CancelSyncJobConfirmation": "Cancelling the sync job will remove synced media from the device during the next sync process. Are you sure you wish to proceed?", + "SyncMedia": "Synkroniser medier", + "HeaderCancelSyncJob": "Afbryd synkronisering", + "CancelSyncJobConfirmation": "Afbrydelse af synkroniseringen vil fjerne medier fra enheden under n\u00e6ste synkroniseringsproces. Er du sikker p\u00e5 du \u00f8nsker at forts\u00e6tte?", "TabSync": "Sync", - "MessagePleaseSelectDeviceToSyncTo": "Please select a device to sync to.", - "MessageSyncJobCreated": "Sync job created.", - "LabelSyncTo": "Sync to:", - "LabelSyncJobName": "Sync job name:", - "LabelQuality": "Quality:", - "HeaderSettings": "Settings", - "OptionAutomaticallySyncNewContent": "Automatically sync new content", - "OptionAutomaticallySyncNewContentHelp": "New content added to this category will be automatically synced to the device.", - "OptionSyncUnwatchedVideosOnly": "Sync unwatched videos only", - "OptionSyncUnwatchedVideosOnlyHelp": "Only unwatched videos will be synced, and videos will be removed from the device as they are watched.", - "LabelItemLimit": "Item limit:", - "LabelItemLimitHelp": "Optional. Set a limit to the number of items that will be synced.", - "MessageBookPluginRequired": "Requires installation of the Bookshelf plugin", - "MessageGamePluginRequired": "Requires installation of the GameBrowser plugin", - "MessageUnsetContentHelp": "Content will be displayed as plain folders. For best results use the metadata manager to set the content types of sub-folders.", - "SyncJobItemStatusQueued": "Queued", - "SyncJobItemStatusConverting": "Converting", - "SyncJobItemStatusTransferring": "Transferring", - "SyncJobItemStatusSynced": "Synced", - "SyncJobItemStatusFailed": "Failed", - "SyncJobItemStatusRemovedFromDevice": "Removed from device", - "SyncJobItemStatusCancelled": "Cancelled", - "LabelProfile": "Profile:", + "MessagePleaseSelectDeviceToSyncTo": "V\u00e6lg en enhed at synkroniserer til.", + "MessageSyncJobCreated": "Synkroniserings job oprettet", + "LabelSyncTo": "Synkroniser til:", + "LabelSyncJobName": "Navn til synkroniserings job:", + "LabelQuality": "Kvalitet:", + "HeaderSettings": "Indstillinger", + "OptionAutomaticallySyncNewContent": "Synkroniser automatisk nyt indhold", + "OptionAutomaticallySyncNewContentHelp": "Nyt indhold i denne kategori vil automatisk blive synkroniseret til enheden.", + "OptionSyncUnwatchedVideosOnly": "Synkroniser kun usete videoer", + "OptionSyncUnwatchedVideosOnlyHelp": "Kun usete videoer vil blive synkroniseret, og videoer vil blive fjernet fra enheden n\u00e5r de er blevet set.", + "LabelItemLimit": "Maks. filer:", + "LabelItemLimitHelp": "Valgfri. S\u00e6t en gr\u00e6nse for antallet af filer der synkroniseres.", + "MessageBookPluginRequired": "Kr\u00e6ver installation af Bookshelf tilf\u00f8jelsen", + "MessageGamePluginRequired": "Kr\u00e6ver installation af GameBrowser tilf\u00f8jelsen", + "MessageUnsetContentHelp": "Indhold vil blive vist som almindelige mapper. For det bedste resultat benyt metadata manageren til at v\u00e6lge indholdstypen i undermapper.", + "SyncJobItemStatusQueued": "Sat i k\u00f8", + "SyncJobItemStatusConverting": "Konverterer", + "SyncJobItemStatusTransferring": "Overf\u00f8rer", + "SyncJobItemStatusSynced": "Synkroniseret", + "SyncJobItemStatusFailed": "Fejlet", + "SyncJobItemStatusRemovedFromDevice": "Fjernet fra enhed", + "SyncJobItemStatusCancelled": "Annulleret", + "LabelProfile": "Profil:", "LabelBitrateMbps": "Bitrate (Mbps):", - "EmbyIntroDownloadMessage": "To download and install Emby Server visit {0}.", - "ButtonNewServer": "New Server", + "EmbyIntroDownloadMessage": "For at downloade og installere Emby bes\u00f8g {0}.", + "ButtonNewServer": "Ny server", "ButtonSignInWithConnect": "Log ind med Emby Connect", "HeaderNewServer": "Ny server", - "MyDevice": "My Device", - "ButtonRemote": "Remote", + "MyDevice": "Min enhed", + "ButtonRemote": "Fjernbetjening", "TabInfo": "Info", "TabCast": "Cast", - "TabScenes": "Scenes" + "TabScenes": "Scener" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/da.json b/MediaBrowser.Server.Implementations/Localization/Server/da.json index 7c483de17..8ac1d44a8 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/da.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/da.json @@ -18,15 +18,15 @@ "WelcomeToProject": "Velkommen til Emby!", "ThisWizardWillGuideYou": "Denne guide vil hj\u00e6lpe dig igennem ops\u00e6tningen. For at begynde, v\u00e6lg venligst dit fortrukne sprog.", "TellUsAboutYourself": "Fort\u00e6l os lidt om dig selv", - "ButtonQuickStartGuide": "Quick start guide", + "ButtonQuickStartGuide": "Hurtig-start guide", "LabelYourFirstName": "Dit fornavn", "MoreUsersCanBeAddedLater": "Flere brugere kan tilf\u00f8jes senere i betjeningspanelet.", "UserProfilesIntro": "Emby har indbygget underst\u00f8ttelse af brugerprofiler. Dette giver hver bruger sine egne indstillinger for visning, afspilningsstatus og for\u00e6ldrekontrol.", "LabelWindowsService": "Windows Service", "AWindowsServiceHasBeenInstalled": "Der er blevet installeret en Windows Service.", - "WindowsServiceIntro1": "Emby Server normally runs as a desktop application with a tray icon, but if you prefer to run it as a background service, it can be started from the windows services control panel instead.", + "WindowsServiceIntro1": "Emby Server k\u00f8rer normalt som en desktop applikation med et statusbar ikon, men hvis du \u00f8nsker at k\u00f8rer det som en baggrundsservice kan programmet startes fra windows services kontrolpanel istedet.", "WindowsServiceIntro2": "Hvis Windows servicen bruges skal du v\u00e6re opm\u00e6rksom p\u00e5, at servicen ikke kan k\u00f8re p\u00e5 samme tid som bakkeikonet. Det er derfor n\u00f8dvendigt at afslutte bakkeikonet f\u00f8r servicen startes. Det er n\u00f8dvendigt at konfigurere servicen til at k\u00f8re med administrative privileger, som kan g\u00f8res via Windows Service kontrolpanelet. V\u00e6r opm\u00e6rksom p\u00e5 at servicen p\u00e5 nuv\u00e6rende tidspunkt ikke er i stand til at autoopdatere, s\u00e5 opdatering vil kr\u00e6ve manuel handling.", - "WizardCompleted": "That's all we need for now. Emby has begun collecting information about your media library. Check out some of our apps, and then click Finish<\/b> to view the Server Dashboard<\/b>.", + "WizardCompleted": "Det er alt vi beh\u00f8ver for nu. Emby er begyndt at indsamle information omkring dit mediebibliotek. Tjek nogle af vores apps og klik derefter p\u00e5 F\u00e6rdig<\/b> for at se Server betjeningspanelet<\/b>.", "LabelConfigureSettings": "Konfigurer indstillinger", "LabelEnableVideoImageExtraction": "Aktiver udtr\u00e6kning af video billede", "VideoImageExtractionHelp": "For videoer der ikke allerede har billeder, og som vi ikke kan finde internet billeder til. Dette vil g\u00f8re den indledende biblioteksskanning l\u00e6ngere, men vil resulterer i en p\u00e6nere pr\u00e6sentation.", @@ -34,23 +34,23 @@ "LabelChapterImageExtractionForMoviesHelp": "Udtr\u00e6kning af kapitelbilleder lader klienter vise billeder i scenev\u00e6lgeren. Denne proces kan v\u00e6re langsom og processorbelastende, og kan kr\u00e6ve adskillige gigabytes harddiskplads. Processen k\u00f8rer som en natlig planlagt opgave, selv om dette kan \u00e6ndres i planl\u00e6ggeren. Det anbefales ikke at k\u00f8re denne proces i tidsrum hvor der er brugere p\u00e5 systemet.", "LabelEnableAutomaticPortMapping": "Aktiver automatisk port kortl\u00e6gning", "LabelEnableAutomaticPortMappingHelp": "UPnP tillader automatisk routerkonfiguration for nem fjernadgang. Dette virker muligvis ikke med alle routere.", - "HeaderTermsOfService": "Emby Terms of Service", - "MessagePleaseAcceptTermsOfService": "Please accept the terms of service and privacy policy before continuing.", - "OptionIAcceptTermsOfService": "I accept the terms of service", - "ButtonPrivacyPolicy": "Privacy policy", - "ButtonTermsOfService": "Terms of Service", - "HeaderDeveloperOptions": "Developer Options", - "OptionEnableWebClientResponseCache": "Enable web client response caching", - "OptionDisableForDevelopmentHelp": "Configure these as needed for web client development purposes.", - "OptionEnableWebClientResourceMinification": "Enable web client resource minification", - "LabelDashboardSourcePath": "Web client source path:", - "LabelDashboardSourcePathHelp": "If running the server from source, specify the path to the dashboard-ui folder. All web client files will be served from this location.", + "HeaderTermsOfService": "Emby tjenestevilk\u00e5r", + "MessagePleaseAcceptTermsOfService": "Accepter venligst tjenestevilk\u00e5rene og privatlivspolitikken f\u00f8r du forts\u00e6tter.", + "OptionIAcceptTermsOfService": "Jeg accepterer tjenestevilk\u00e5rene", + "ButtonPrivacyPolicy": "Privatlivspolitik", + "ButtonTermsOfService": "Tjenestevilk\u00e5r", + "HeaderDeveloperOptions": "Indstillinger for udviklere", + "OptionEnableWebClientResponseCache": "Aktiver webklient svar-caching", + "OptionDisableForDevelopmentHelp": "Konfigurer disse som n\u00f8dvendigt for udviklingsform\u00e5l af webklienten", + "OptionEnableWebClientResourceMinification": "Aktiver formindskelse af webklientens forbrug af ressourcer", + "LabelDashboardSourcePath": "Webklient kildesti:", + "LabelDashboardSourcePathHelp": "Hvis serveren k\u00f8rer fra kilden, specificer da stien til dashboard-ui mappen. Alle webklient-filer vil blive leveret fra denne lokation.", "ButtonConvertMedia": "Konverter medie", "ButtonOrganize": "Organiser", "LinkedToEmbyConnect": "Koblet til Emby Connect", - "HeaderSupporterBenefits": "Supporter Benefits", + "HeaderSupporterBenefits": "Supporter fordele", "HeaderAddUser": "Tilf\u00f8j bruger", - "LabelAddConnectSupporterHelp": "To add a user who isn't listed, you'll need to first link their account to Emby Connect from their user profile page.", + "LabelAddConnectSupporterHelp": "For at tilf\u00f8je en bruger som ikke er angivet skal du f\u00f8rst sammenk\u00e6de deres konto til Emby Connect fra deres brugers profilside.", "LabelPinCode": "Pinkode:", "OptionHideWatchedContentFromLatestMedia": "Skjul sete fra seneste", "HeaderSync": "Sync", @@ -59,7 +59,7 @@ "ButtonExit": "Afslut", "ButtonNew": "Ny", "HeaderTV": "TV", - "HeaderAudio": "Audio", + "HeaderAudio": "Lyd", "HeaderVideo": "Video", "HeaderPaths": "Stier", "CategorySync": "Sync", @@ -75,10 +75,10 @@ "ButtonConfigurePinCode": "Konfigurer pinkode", "HeaderAdultsReadHere": "Voksne l\u00e6s her!", "RegisterWithPayPal": "Registrer med PayPal", - "HeaderSyncRequiresSupporterMembership": "Sync Requires a Supporter Membership", + "HeaderSyncRequiresSupporterMembership": "Sync kr\u00e6ver medlemsskab", "HeaderEnjoyDayTrial": "Nyd en 14-dages gratis pr\u00f8veperiode", "LabelSyncTempPath": "Sti for midlertidige filer:", - "LabelSyncTempPathHelp": "Specify a custom sync working folder. Converted media created during the sync process will be stored here.", + "LabelSyncTempPathHelp": "Specificer en brugerdefineret synkroniserings arbejds-mappe. Konverterede filer vil under synkroniseringsprocessen blive gemt her.", "LabelCustomCertificatePath": "Sti til eget certifikat:", "LabelCustomCertificatePathHelp": "Angiv dit eget ssl certifikat som .pfx fil. Hvis du undlader dette, danner serveren et selvsigneret certifikat.", "TitleNotifications": "Underretninger", @@ -90,7 +90,7 @@ "LabelEnableEnhancedMovies": "Aktiver udvidede filmvisninger", "LabelEnableEnhancedMoviesHelp": "Aktiver dette for at f\u00e5 vist film som mapper med trailere, medvirkende og andet relateret inhold.", "HeaderSyncJobInfo": "Sync Job", - "FolderTypeMixed": "Mixed content", + "FolderTypeMixed": "Blandet indhold", "FolderTypeMovies": "FIlm", "FolderTypeMusic": "Musik", "FolderTypeAdultVideos": "Voksenfilm", @@ -110,7 +110,7 @@ "LabelCountry": "Land:", "LabelLanguage": "Sprog:", "LabelTimeLimitHours": "Tidsgr\u00e6nse (timer):", - "ButtonJoinTheDevelopmentTeam": "Join the Development Team", + "ButtonJoinTheDevelopmentTeam": "Bliv medlem af Teamet bag Emby", "HeaderPreferredMetadataLanguage": "Foretrukket sprog for metadata:", "LabelSaveLocalMetadata": "Gem illustrationer og metadata i mediemapper", "LabelSaveLocalMetadataHelp": "Lagring af illustrationer og metadata i mediemapper vil placerer dem et sted hvor de nemt kan redigeres.", @@ -214,17 +214,17 @@ "OptionArtist": "Artist", "OptionAlbum": "Album", "OptionTrackName": "Nummerets navn", - "OptionCommunityRating": "Community Rating", + "OptionCommunityRating": "F\u00e6llesskabsvurdering", "OptionNameSort": "Navn", "OptionFolderSort": "Mapper", "OptionBudget": "Budget", "OptionRevenue": "Indt\u00e6gt", "OptionPoster": "Plakat", - "OptionPosterCard": "Poster card", - "OptionBackdrop": "Backdrop", + "OptionPosterCard": "Plakat", + "OptionBackdrop": "Baggrund", "OptionTimeline": "Tidslinje", - "OptionThumb": "Thumb", - "OptionThumbCard": "Thumb card", + "OptionThumb": "Miniature", + "OptionThumbCard": "Miniature kort", "OptionBanner": "Banner", "OptionCriticRating": "Kritikervurdering", "OptionVideoBitrate": "Video Bitrate", @@ -240,7 +240,7 @@ "HeaderLatestSongs": "Seneste sange", "HeaderRecentlyPlayed": "Afspillet for nyligt", "HeaderFrequentlyPlayed": "Ofte afspillet", - "DevBuildWarning": "Dev builds are the bleeding edge. Released often, these build have not been tested. The application may crash and entire features may not work at all.", + "DevBuildWarning": "Udviklerversionen er bleeding edge. Nye versioner bliver ofte udgivet og bliver ikke testet inden. Applikationen kan risikere at lukke ned og funktioner kan nogle gange slet ikke virke.", "LabelVideoType": "Video type:", "OptionBluray": "Bluray", "OptionDvd": "Dvd", @@ -296,7 +296,7 @@ "SearchKnowledgeBase": "S\u00f8g i vidensdatabasen", "VisitTheCommunity": "Bes\u00f8g f\u00e6llesskabet", "VisitProjectWebsite": "Bes\u00f8g Embys hjemmeside", - "VisitProjectWebsiteLong": "Visit the Emby Web site to catch the latest news and keep up with the developer blog.", + "VisitProjectWebsiteLong": "Bes\u00f8g Emby hjemmesiden for at blive opdateret p\u00e5 de seneste nyheder og holde dig opdateret med udviklernes blog.", "OptionHideUser": "Vis ikke denne bruger p\u00e5 loginsiden", "OptionHideUserFromLoginHelp": "Nyttigt for private kontoer eller skjulte administratorkontoer. Brugeren skal logge ind ved at skive sit brugernavn og adgangskode.", "OptionDisableUser": "Deaktiver denne bruger", @@ -323,7 +323,7 @@ "ButtonAddToCollection": "Tilf\u00f8j til samling", "PismoMessage": "Utilizing Pismo File Mount through a donated license.", "TangibleSoftwareMessage": "Utilizing Tangible Solutions Java\/C# converters through a donated license.", - "HeaderCredits": "Credits", + "HeaderCredits": "Anerkendelser", "PleaseSupportOtherProduces": "St\u00f8t venligst andre gratis produkter vi bruger:", "VersionNumber": "Version {0}", "TabPaths": "Stier", @@ -370,8 +370,8 @@ "ExtractChapterImagesHelp": "Udtr\u00e6kning af kapitelbilleder lader klienter vise billeder i scenev\u00e6lgeren. Denne proces kan v\u00e6re langsom og processorbelastende, og kan kr\u00e6ve adskillige gigabytes harddiskplads. Processen k\u00f8rer som en natlig planlagt opgave, selv om dette kan \u00e6ndres i planl\u00e6ggeren. Det anbefales ikke at k\u00f8re denne proces i tidsrum hvor der er brugere p\u00e5 systemet.", "LabelMetadataDownloadLanguage": "Foretrukket sprog for nedhentning:", "ButtonAutoScroll": "Rul automatisk", - "LabelImageSavingConvention": "Image saving convention:", - "LabelImageSavingConventionHelp": "Emby recognizes images from most major media applications. Choosing your downloading convention is useful if you also use other products.", + "LabelImageSavingConvention": "Konvention for lagring af billeder:", + "LabelImageSavingConventionHelp": "Emby genkender billeder fra de fleste store medieapplikationer. Valg af konvention for hentning af billeder er nyttefuld hvis du ogs\u00e5 benytter dig af andre produkter.", "OptionImageSavingCompatible": "Kompatibel - Emby\/Kodi\/Plex", "OptionImageSavingStandard": "Standard - MB2", "ButtonSignIn": "Log Ind", @@ -434,7 +434,7 @@ "OptionDownloadDiscImage": "Disk", "OptionDownloadBannerImage": "Banner", "OptionDownloadBackImage": "Bagside", - "OptionDownloadArtImage": "Art", + "OptionDownloadArtImage": "Kunst", "OptionDownloadPrimaryImage": "Prim\u00e6r", "HeaderFetchImages": "Hent billeder:", "HeaderImageSettings": "Billedindstillinger", @@ -482,10 +482,10 @@ "HeaderAwardsAndReviews": "Priser og anmelselser", "HeaderSoundtracks": "Soundtracks", "HeaderMusicVideos": "Musikvideoer", - "HeaderSpecialFeatures": "Special Features", - "HeaderCastCrew": "Cast & Crew", - "HeaderAdditionalParts": "Additional Parts", - "ButtonSplitVersionsApart": "Split Versions Apart", + "HeaderSpecialFeatures": "Specielle egenskaber", + "HeaderCastCrew": "Medvirkende", + "HeaderAdditionalParts": "Andre stier:", + "ButtonSplitVersionsApart": "Opdel versioner", "ButtonPlayTrailer": "Trailer", "LabelMissing": "Mangler", "LabelOffline": "Offline", @@ -497,7 +497,7 @@ "LabelTo": "Til:", "LabelToHelp": "F. eks. \\\\MyServer\\Movies (en sti klienterne kan tilg\u00e5)", "ButtonAddPathSubstitution": "Tilf\u00f8j substitution", - "OptionSpecialEpisode": "Specials", + "OptionSpecialEpisode": "S\u00e6rudsendelser", "OptionMissingEpisode": "Manglende episoder", "OptionUnairedEpisode": "Ikke sendte episoder", "OptionEpisodeSortName": "Navn for sortering af episoder", @@ -648,7 +648,7 @@ "LabelSupporterKey": "Supporter n\u00f8gle (Kopier fra e-mail og inds\u00e6t)", "LabelSupporterKeyHelp": "Inds\u00e6t din supporter n\u00f8gle for at f\u00e5 yderligere fordele, f\u00e6llesskabet har udviklet til Emby.", "MessageInvalidKey": "Supporter n\u00f8glen manler eller er ugyldig.", - "ErrorMessageInvalidKey": "In order for any premium content to be registered, you must also be an Emby Supporter. Please donate and support the continued development of the core product. Thank you.", + "ErrorMessageInvalidKey": "For at registrere premium indhold skal du v\u00e6re en Emby Supporter. Doner venligst for at st\u00f8tte den l\u00f8bende udvikling af vores kerneprodukt. Mange tak.", "HeaderDisplaySettings": "Indstillinger for visning", "TabPlayTo": "Afspil til", "LabelEnableDlnaServer": "Aktiver DNLA server", @@ -700,7 +700,7 @@ "CategoryApplication": "Program", "CategoryPlugin": "Plugin", "LabelMessageTitle": "Titel p\u00e5 besked", - "LabelAvailableTokens": "Available tokens:", + "LabelAvailableTokens": "Tilg\u00e6ngelige tokens:", "AdditionalNotificationServices": "Kig i plugin-kataloget for at f\u00e5 yderligere uderretnings-tjenester", "OptionAllUsers": "Alle brugere", "OptionAdminUsers": "Administratore", @@ -743,7 +743,7 @@ "ButtonVolumeDown": "Volume -", "ButtonMute": "Lyd fra", "HeaderLatestMedia": "Seneste medier", - "OptionSpecialFeatures": "Special Features", + "OptionSpecialFeatures": "Specielle egenskaber", "HeaderCollections": "Samlinger", "LabelProfileCodecsHelp": "Adskil med komma. Kan efterlades tom for at g\u00e6lde for alle codecs.", "LabelProfileContainersHelp": "Adskil med komma. Kan efterlades tom for at g\u00e6lde for alle containere.", @@ -791,8 +791,8 @@ "LabelIconMaxWidthHelp": "Maksimumopl\u00f8sningen p\u00e5 ikoner der bliver vist med upnp:icon", "LabelIconMaxHeight": "Max h\u00f8jde p\u00e5 ikoner:", "LabelIconMaxHeightHelp": "Maksimumopl\u00f8sningen p\u00e5 ikoner der bliver vist med upnp:icon", - "LabelIdentificationFieldHelp": "A case-insensitive substring or regex expression.", - "HeaderProfileServerSettingsHelp": "These values control how Emby Server will present itself to the device.", + "LabelIdentificationFieldHelp": "En case-insensitive substring eller regex ekspression.", + "HeaderProfileServerSettingsHelp": "Disse v\u00e6rdier kontrollerer hvordan Emby pr\u00e6senterer sig til enheden.", "LabelMaxBitrate": "Max bitrate:", "LabelMaxBitrateHelp": "Angiv en maksimal bitrate i omr\u00e5der med begr\u00e6nset b\u00e5ndbredde, eller hvis enheden selv har begr\u00e6nsninger.", "LabelMaxStreamingBitrate": "Max streaming bitrate:", @@ -804,8 +804,8 @@ "LabelMusicStaticBitrateHelp": "Angiv en maksimal bitrate n\u00e5r der synkroniseres musik.", "LabelMusicStreamingTranscodingBitrate": "Bitrate for musiktranskodning", "LabelMusicStreamingTranscodingBitrateHelp": "Angiv en maksimal bitrate n\u00e5r der streames musik.", - "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests", - "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.", + "OptionIgnoreTranscodeByteRangeRequests": "Ignorer foresp\u00f8rgsler vedr\u00f8rende transcode byte interval", + "OptionIgnoreTranscodeByteRangeRequestsHelp": "Hvis aktiveret vil disse foresp\u00f8rgsler blive efterkommet, men byte range headeren ignoreret.", "LabelFriendlyName": "System venligt navn", "LabelManufacturer": "Producent", "LabelManufacturerUrl": "Producent url", @@ -1163,7 +1163,7 @@ "OptionLocalRefreshOnly": "Opdater kun lokalt", "HeaderRefreshMetadata": "Opdater metadata", "HeaderPersonInfo": "Personinformation", - "HeaderIdentifyItem": "Identify Item", + "HeaderIdentifyItem": "Identificer genstand", "HeaderIdentifyItemHelp": "Indtast et eller flere s\u00f8gekriterier.Fjern kriterier for at f\u00e5 flere s\u00f8geresultater.", "HeaderConfirmDeletion": "Bekr\u00e6ft sletning", "LabelFollowingFileWillBeDeleted": "F\u00f8lgende filer bliver slettet:", @@ -1190,7 +1190,7 @@ "LabelAirTime:": "Sendetid:", "LabelRuntimeMinutes": "Spilletid (minutter):", "LabelParentalRating": "Aldersgr\u00e6nse:", - "LabelCustomRating": "Custom rating:", + "LabelCustomRating": "Brugerdefineret bed\u00f8mmelse:", "LabelBudget": "Budget", "LabelRevenue": "Indt\u00e6gter ($):", "LabelOriginalAspectRatio": "Originalt formatforhold:", @@ -1228,7 +1228,7 @@ "OptionNoThemeVideo": "Ingen temavideo", "LabelOneTimeDonationAmount": "Donationsbel\u00f8b:", "ButtonDonate": "Don\u00e9r", - "ButtonPurchase": "Purchase", + "ButtonPurchase": "K\u00f8b", "OptionActor": "Skuespiller", "OptionComposer": "Komponist", "OptionDirector": "Instrukt\u00f8r", @@ -1335,10 +1335,10 @@ "OptionWeekends": "Weekender", "MessageProfileInfoSynced": "Brugerprofil synkroniseret med Emby Connect", "HeaderOptionalLinkEmbyAccount": "Valgfrit: Forbind din Emby konto", - "ButtonTrailerReel": "Trailer reel", - "HeaderTrailerReel": "Trailer Reel", + "ButtonTrailerReel": "Trailer rulle", + "HeaderTrailerReel": "Trailer rulle", "OptionPlayUnwatchedTrailersOnly": "Afspil kun ikke sete trailere", - "HeaderTrailerReelHelp": "Start a trailer reel to play a long running playlist of trailers.", + "HeaderTrailerReelHelp": "Start en trailer rulle for at afspille en lang afspilningsliste med forfilm.", "MessageNoTrailersFound": "Ingen trailere fundet. Installer Trailer kanalen for at tilf\u00f8je et bibliotek med trailere fra internettet.", "HeaderNewUsers": "Nye brugere", "ButtonSignUp": "Tilmeld dig", @@ -1381,7 +1381,7 @@ "TabJobs": "Opgaver", "TabSyncJobs": "Sync opgaver", "LabelTagFilterMode": "Tilstand:", - "LabelTagFilterAllowModeHelp": "If allowed tags are used as part of a deeply nested folder structure, content that is tagged will require parent folders to be tagged as well.", + "LabelTagFilterAllowModeHelp": "Hvis godkendte tags bliver brugt som en del af en meget forgrenet mappestruktur, kr\u00e6ver tagget indhold at parent mappen ogs\u00e5 tagges.", "HeaderThisUserIsCurrentlyDisabled": "Denne bruger er for \u00f8jeblikket deaktiveret.", "MessageReenableUser": "Se nedenfor om genaktivering", "LabelEnableInternetMetadataForTvPrograms": "Hent internet metadata for:", @@ -1425,11 +1425,11 @@ "LabelServerPort": "Port:", "HeaderNewServer": "Ny server", "ButtonChangeServer": "Skift server", - "HeaderConnectToServer": "Connect to Server", - "OptionReportList": "List View", - "OptionReportStatistics": "Statistics", - "OptionReportGrouping": "Grouping", - "HeaderExport": "Export", - "HeaderColumns": "Columns", + "HeaderConnectToServer": "Forbind til server", + "OptionReportList": "Liste visning", + "OptionReportStatistics": "Statistik", + "OptionReportGrouping": "Gruppering", + "HeaderExport": "Eksporter", + "HeaderColumns": "S\u00f8jler", "ButtonReset": "Reset" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/fr.json b/MediaBrowser.Server.Implementations/Localization/Server/fr.json index be2c5279a..38d55bc6f 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/fr.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/fr.json @@ -1228,7 +1228,7 @@ "OptionNoThemeVideo": "Pas de th\u00e8me vid\u00e9o", "LabelOneTimeDonationAmount": "Montant du don :", "ButtonDonate": "Faire un don", - "ButtonPurchase": "Purchase", + "ButtonPurchase": "Acheter", "OptionActor": "Acteur(trice)", "OptionComposer": "Compositeur:", "OptionDirector": "R\u00e9alisateur:", diff --git a/MediaBrowser.Server.Implementations/Localization/Server/kk.json b/MediaBrowser.Server.Implementations/Localization/Server/kk.json index e13011d7d..72a2ab086 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/kk.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/kk.json @@ -1228,7 +1228,7 @@ "OptionNoThemeVideo": "\u0422\u0430\u049b\u044b\u0440\u044b\u043f\u0442\u044b\u049b \u0431\u0435\u0439\u043d\u0435\u0441\u0456\u0437", "LabelOneTimeDonationAmount": "\u049a\u0430\u0439\u044b\u0440\u043c\u0430\u043b\u0434\u044b\u049b \u049b\u043e\u0440\u044b\u0442\u044b\u043d\u0434\u044b\u0441\u044b:", "ButtonDonate": "\u049a\u0430\u0439\u044b\u0440\u043c\u0430\u043b\u0430\u0443", - "ButtonPurchase": "Purchase", + "ButtonPurchase": "\u0421\u0430\u0442\u044b\u043f \u0430\u043b\u0443", "OptionActor": "\u0410\u043a\u0442\u0435\u0440", "OptionComposer": "\u041a\u043e\u043c\u043f\u043e\u0437\u0438\u0442\u043e\u0440", "OptionDirector": "\u0420\u0435\u0436\u0438\u0441\u0441\u0435\u0440", diff --git a/MediaBrowser.Server.Implementations/Localization/Server/pt-BR.json b/MediaBrowser.Server.Implementations/Localization/Server/pt-BR.json index 5c196cfb2..54d523825 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/pt-BR.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/pt-BR.json @@ -1228,7 +1228,7 @@ "OptionNoThemeVideo": "Nenhum V\u00eddeo-tema", "LabelOneTimeDonationAmount": "Valor da doa\u00e7\u00e3o:", "ButtonDonate": "Doar", - "ButtonPurchase": "Purchase", + "ButtonPurchase": "Comprar", "OptionActor": "Ator", "OptionComposer": "Compositor", "OptionDirector": "Diretor", diff --git a/MediaBrowser.Server.Implementations/Localization/Server/ru.json b/MediaBrowser.Server.Implementations/Localization/Server/ru.json index 138175f3b..f09cb3594 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/ru.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/ru.json @@ -1228,7 +1228,7 @@ "OptionNoThemeVideo": "\u0411\u0435\u0437 \u0442\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0432\u0438\u0434\u0435\u043e", "LabelOneTimeDonationAmount": "\u0421\u0443\u043c\u043c\u0430 \u043f\u043e\u0436\u0435\u0440\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u044f:", "ButtonDonate": "\u041f\u043e\u0436\u0435\u0440\u0442\u0432\u043e\u0432\u0430\u0442\u044c", - "ButtonPurchase": "Purchase", + "ButtonPurchase": "\u041f\u0440\u0438\u043e\u0431\u0440\u0435\u0441\u0442\u0438", "OptionActor": "\u0410\u043a\u0442\u0451\u0440", "OptionComposer": "\u041a\u043e\u043c\u043f\u043e\u0437\u0438\u0442\u043e\u0440", "OptionDirector": "\u0420\u0435\u0436\u0438\u0441\u0441\u0451\u0440", diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index 579df55a8..9d45451a8 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -1243,7 +1243,7 @@ "OptionNoThemeVideo": "No Theme Video", "LabelOneTimeDonationAmount": "Donation amount:", "ButtonDonate": "Donate", - "ButtonPurchase": "Purchase", + "ButtonPurchase": "Purchase", "OptionActor": "Actor", "OptionComposer": "Composer", "OptionDirector": "Director", @@ -1447,5 +1447,6 @@ "OptionReportGrouping": "Grouping", "HeaderExport": "Export", "HeaderColumns": "Columns", - "ButtonReset": "Reset" + "ButtonReset": "Reset", + "OptionEnableExternalVideoPlayers": "Enable external video players" } diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 13234888c..f6601ce80 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -581,7 +581,6 @@ namespace MediaBrowser.WebDashboard.Api "cinemamodeconfiguration.js", "encodingsettings.js", - "externalplayer.js", "forgotpassword.js", "forgotpasswordpin.js", "indexpage.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index acac62471..4315c2023 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -231,6 +231,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/SharedVersion.cs b/SharedVersion.cs index 2a2ce1e51..1f434195a 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; -[assembly: AssemblyVersion("3.0.*")] -//[assembly: AssemblyVersion("3.0.5621.1")] +//[assembly: AssemblyVersion("3.0.*")] +[assembly: AssemblyVersion("3.0.5621.2")] -- cgit v1.2.3