/// <summary>
        /// Ergänzt die Beschreibung der aktuellen Aufzeichnungen.
        /// </summary>
        /// <param name="info">Die Beschreibung der Gesamtaufgabe des aktuellen Aufzeichnungsprozesses.</param>
        /// <param name="finalCall">Gesetzt, wenn es sich um den abschließenden Aufruf handelt. Dieser
        /// wird ausschließlich für die Erstellung des Protokolleintrags verwendet.</param>
        /// <param name="state">Der aktuelle Zustand.</param>
        protected override void OnFillInformation( FullInfo info, bool finalCall, ServerInformation state )
        {
            // Set static data
            info.IsDynamic = true;
            info.CanStream = true;

            // Synchronize access
            lock (m_recordings)
            {
                // Load all files we've seen so far
                info.Recording.RecordingFiles.Clear();
                info.Recording.RecordingFiles.AddRange( m_files );

                // Create the file mapping
                var files =
                    m_files
                        .Where( file => !string.IsNullOrEmpty( file.ScheduleIdentifier ) )
                        .GroupBy( file => new Guid( file.ScheduleIdentifier ), file => file.Path )
                        .ToDictionary( group => group.Key, group => group.ToArray() );

                // Process all recordings
                info
                    .Streams
                    .AddRange( (finalCall ? m_allRecordings : m_recordings)
                        .Select( recording =>
                            {
                                // Collect the data
                                var streamInfo = (state == null) ? null : state.Streams.Find( recording.Match );
                                var target = (streamInfo == null) ? null : streamInfo.StreamTarget;

                                // Create record
                                return StreamInfo.Create( recording, target, files );
                            } ) );
            }
        }
 /// <summary>
 /// Ergänzt den aktuellen Zustand des Suchlaufs.
 /// </summary>
 /// <param name="info">Die bereits vorbereitete Informationsstruktur.</param>
 /// <param name="finalCall">Gesetzt, wenn dieser Zustand als Protokoll verwendet wird.</param>
 /// <param name="state">Der zuletzt erhaltene Zustand.</param>
 protected override void OnFillInformation( FullInfo info, bool finalCall, ServerInformation state )
 {
     // Copy current result
     if (state == null)
         info.Recording.TotalSize = 0;
     else if (!state.UpdateProgress.HasValue)
         info.Recording.TotalSize = 0;
     else
         info.Recording.TotalSize = (uint) state.UpdateSourceCount;
 }
        /// <summary>
        /// Erstellt eine neue Liste von Beschreibungen für eine aktive Aufzeichnung.
        /// </summary>
        /// <param name="active">Die Daten zur aktiven Aufzeichnung.</param>
        /// <param name="server">Der zugehörige Dienst.</param>
        /// <returns>Die gewünschten Beschreibungen.</returns>
        public static PlanCurrent[] Create( FullInfo active, VCRServer server )
        {
            // Validate
            if (active == null)
                throw new ArgumentNullException( "active" );

            // Validate
            var recording = active.Recording;
            if (recording == null)
                throw new ArgumentNullException( "recording" );

            // Multiple recordings
            var streams = active.Streams;
            if (streams != null)
                if (streams.Count > 0)
                    return streams.SelectMany( ( stream, index ) => Create( active, stream, index, server ) ).ToArray();

            // Single recording - typically a task
            var start = RoundToSecond( active.Recording.PhysicalStart.GetValueOrDefault( DateTime.UtcNow ) );
            var end = RoundToSecond( recording.EndsAt );
            var source = recording.Source;
            var sourceName = source.DisplayName;

            // Create
            var current =
                new PlanCurrent
                {
                    PlanIdentifier = recording.ScheduleUniqueID.Value.ToString( "N" ),
                    ProfileName = source.ProfileName,
                    Duration = end - start,
                    Name = recording.Name,
                    m_source = source,
                    StartTime = start,
                    Files = _NoFiles,
                    IsLate = false,
                    Index = -1,
                };

            // Finish            
            if (VCRJob.ProgramGuideName.Equals( sourceName ))
                current.SizeHint = $"{recording.TotalSize:N0} Einträge";
            else if (VCRJob.SourceScanName.Equals( sourceName ))
                current.SizeHint = $"{recording.TotalSize:N0} Quellen";
            else if (VCRJob.ZappingName.Equals( sourceName ))
                current.SizeHint = GetSizeHint( recording.TotalSize );
            else
                current.Complete( server );

            // Report
            return new[] { current };
        }
        /// <summary>
        /// Erstellt eine Beschreibung zu einer einzelnen Aufzeichnung auf einem Gerät.
        /// </summary>
        /// <param name="active">Beschreibt die gesamte Aufzeichnung.</param>
        /// <param name="stream">Die zu verwendende Teilaufzeichnung.</param>
        /// <param name="streamIndex">Die laufende Nummer dieses Datenstroms.</param>
        /// <param name="server">Der zugehörige Dienst.</param>
        /// <returns>Die gewünschte Beschreibung.</returns>
        private static IEnumerable<PlanCurrent> Create( FullInfo active, StreamInfo stream, int streamIndex, VCRServer server )
        {
            // Static data
            var recording = active.Recording;
            var profileName = recording.Source.ProfileName;
            var sizeHint = GetSizeHint( recording.TotalSize ) + " (Gerät)";

            // Process all - beginning with VCR.NET 4.1 there is only one schedule per stream
            foreach (var scheduleInfo in stream.Schedules)
            {
                // Try to locate the context
                var job = string.IsNullOrEmpty( scheduleInfo.JobUniqueID ) ? null : server.JobManager[new Guid( scheduleInfo.JobUniqueID )];
                var schedule = ((job == null) || string.IsNullOrEmpty( scheduleInfo.ScheduleUniqueID )) ? null : job[new Guid( scheduleInfo.ScheduleUniqueID )];

                // Create
                var start = RoundToSecond( scheduleInfo.StartsAt );
                var end = RoundToSecond( scheduleInfo.EndsAt );
                var current =
                    new PlanCurrent
                    {
                        Identifier = (schedule == null) ? null : ServerRuntime.GetUniqueWebId( job, schedule ),
                        PlanIdentifier = scheduleInfo.ScheduleUniqueID,
                        Files = scheduleInfo.Files ?? _NoFiles,
                        StreamTarget = stream.StreamsTo,
                        m_source = scheduleInfo.Source,
                        ProfileName = profileName,
                        Name = scheduleInfo.Name,
                        Duration = end - start,
                        Index = streamIndex,
                        SizeHint = sizeHint,
                        StartTime = start,
                    };

                // Finish
                current.Complete( server );

                // Report
                yield return current;
            }
        }
 /// <summary>
 /// Ermittelt eine Beschreibung der aktuellen Aufzeichnung.
 /// </summary>
 /// <param name="info">Vorabinformation der Basisklasse.</param>
 /// <param name="finalCall">Gesetzt, wenn das Ergebnis zum Protokolleintrag wird.</param>
 /// <param name="state">Die zuletzt erhaltenen Zustandsinformationen.</param>
 protected override void OnFillInformation( FullInfo info, bool finalCall, ServerInformation state )
 {
     // Just copy the progress
     if (state == null)
         info.Recording.TotalSize = 0;
     else if (!state.ProgramGuideProgress.HasValue)
         info.Recording.TotalSize = 0;
     else
         info.Recording.TotalSize = (uint) state.CurrentProgramGuideItems;
 }