/// <summary> /// Erzeugt eine Beschreibung. /// </summary> /// <param name="job">Ein Auftrag.</param> /// <param name="active">Gesetzt, wenn es sich um einen aktiven Auftrag handel.</param> /// <returns>Die gewünschte Beschreibung.</returns> public static ProfileJobInfo Create( VCRJob job, bool active ) { // Process if (active) return new ProfileJobInfo { Profile = job.Source.ProfileName, Name = job.Name, JobIdentifier = job.UniqueID.Value.ToString( "N" ).ToLower() }; else return null; }
/// <summary> /// Entfernt einen Auftrag. /// </summary> /// <param name="job">Der betroffene Auftrag.</param> public void DeleteJob( VCRJob job ) { // Client is talking to us - do not hibernate after change m_PendingHibernation = false; // Remove from job manager JobManager.Delete( job ); // Recalculate BeginNewPlan(); }
/// <summary> /// Legt eine neue Information an. /// </summary> /// <param name="job">Der Auftrag.</param> /// <param name="schedule">Die Aufzeichnung.</param> /// <param name="guide">Ein Eintrag der Programmzeitschrift.</param> /// <param name="profile">Vorgabe für das Geräteprofil.</param> /// <returns>Die Information.</returns> public static JobScheduleInfo Create( VCRJob job, VCRSchedule schedule, ProgramGuideEntry guide, string profile ) { // Process return new JobScheduleInfo { ScheduleIdentifier = (schedule == null) ? null : schedule.UniqueID.Value.ToString( "N" ), JobIdentifier = (job == null) ? null : job.UniqueID.Value.ToString( "N" ), Schedule = EditSchedule.Create( schedule, job, guide ), Job = EditJob.Create( job, guide, profile ), }; }
/// <summary> /// Meldet die Daten zu einer Aufzeichnung. /// </summary> /// <param name="schedule">Die Aufzeichnung.</param> /// <param name="job">Der zugehörige Auftrag.</param> /// <returns></returns> public static InfoSchedule Create( VCRSchedule schedule, VCRJob job ) { // Create return new InfoSchedule { Source = (schedule.Source ?? job.Source).GetUniqueName(), WebId = ServerRuntime.GetUniqueWebId( job, schedule ), StartTime = schedule.FirstStart, RepeatPattern = schedule.Days, Duration = schedule.Duration, Name = schedule.Name, }; }
/// <summary> /// Erstellt eine neue Beschreibung. /// </summary> /// <param name="job">Ein Auftrag.</param> /// <param name="active">Gesetzt, wenn es sich um einen aktiven Auftrag handelt.</param> /// <returns>Die gewünschte Beschreibung.</returns> public static InfoJob Create( VCRJob job, bool active ) { // Report return new InfoJob { Schedules = job.Schedules.Select( schedule => InfoSchedule.Create( schedule, job ) ).OrderBy( schedule => schedule.Name ?? string.Empty, StringComparer.InvariantCultureIgnoreCase ).ToArray(), WebId = ServerRuntime.GetUniqueWebId( job, null ), ProfileName = job.Source.ProfileName, SourceName = job.Source.DisplayName, IsActive = active, Name = job.Name, }; }
/// <summary> /// Aktualisiert einen Auftrag oder legt einen neue an. /// </summary> /// <param name="job">Der betroffene Auftrag.</param> /// <param name="scheduleIdentifier">Die eindeutige Kennung der veränderten Aufzeichnung.</param> public void UpdateJob( VCRJob job, Guid? scheduleIdentifier ) { // Client is talking to us - do not hibernate after update m_PendingHibernation = false; // Cleanup if (scheduleIdentifier.HasValue) foreach (var schedule in job.Schedules) if (schedule.UniqueID.HasValue) if (schedule.UniqueID.Value.Equals( scheduleIdentifier.Value )) schedule.NoStartBefore = null; // Add to job manager JobManager.Update( job, scheduleIdentifier ); // Recalculate BeginNewPlan(); }
/// <summary> /// Erstellt eine neue Beschreibung. /// </summary> /// <param name="job">Der konkrete Auftag.</param> /// <param name="guide">Ein Eintrag der Programmzeitschrift.</param> /// <param name="profile">Vorgabe für das Geräteprofil.</param> /// <returns>Die zugehörige Beschreibung.</returns> public static EditJob Create( VCRJob job, ProgramGuideEntry guide, string profile ) { // Process if (job == null) { // No hope if (guide == null) return null; // Create from program guide return new EditJob { Source = ServerRuntime.VCRServer.GetUniqueName( new SourceSelection { ProfileName = profile, Source = guide.Source } ), DVBSubtitles = UserProfileSettings.UseSubTitles, DolbyDigital = UserProfileSettings.UseAC3, AllLanguages = UserProfileSettings.UseMP2, Videotext = UserProfileSettings.UseTTX, UseProfileForRecording = false, Name = guide.Name.MakeValid(), Profile = profile, }; } // Optionen ermitteln var streams = job.Streams; var sourceName = ServerRuntime.VCRServer.GetUniqueName( job.Source ); // Report return new EditJob { UseProfileForRecording = !job.AutomaticResourceSelection, DolbyDigital = streams.GetUsesDolbyAudio(), AllLanguages = streams.GetUsesAllAudio(), DVBSubtitles = streams.GetUsesSubtitles(), Videotext = streams.GetUsesVideotext(), RecordingDirectory = job.Directory, Profile = job.Source.ProfileName, Source = sourceName, Name = job.Name, }; }
/// <summary> /// Registriert diese Aufzeichnung in einer Planungsinstanz. /// </summary> /// <param name="scheduler">Die zu verwendende Planungsinstanz.</param> /// <param name="job">Der zugehörige Auftrag.</param> /// <param name="devices">Die Liste der Geräte, auf denen die Aufzeichnung ausgeführt werden darf.</param> /// <param name="findSource">Dient zum Prüfen einer Quelle.</param> /// <param name="disabled">Alle deaktivierten Aufträge.</param> /// <param name="context">Die aktuelle Planungsumgebung.</param> /// <exception cref="ArgumentNullException">Es wurden nicht alle Parameter angegeben.</exception> public void AddToScheduler(RecordingScheduler scheduler, VCRJob job, IScheduleResource[] devices, Func <SourceSelection, SourceSelection> findSource, Func <Guid, bool> disabled, PlanContext context) { // Validate if (scheduler == null) { throw new ArgumentNullException(nameof(scheduler)); } if (job == null) { throw new ArgumentNullException(nameof(job)); } if (findSource == null) { throw new ArgumentNullException(nameof(findSource)); } // Let VCR.NET choose a profile to do the work if (job.AutomaticResourceSelection) { devices = null; } // Create the source selection var persistedSource = Source ?? job.Source; var selection = findSource(persistedSource); // Station no longer available if (selection == null) { if (persistedSource != null) { selection = new SourceSelection { DisplayName = persistedSource.DisplayName, ProfileName = persistedSource.ProfileName, Location = persistedSource.Location, Group = persistedSource.Group, Source = new Station { TransportStream = persistedSource.Source?.TransportStream ?? 0, Network = persistedSource.Source?.Network ?? 0, Service = persistedSource.Source?.Service ?? 0, Name = persistedSource.DisplayName, }, } } } ; // See if we are allowed to process var identifier = UniqueID.Value; if (disabled != null) { if (disabled(identifier)) { return; } } // Load all var name = string.IsNullOrEmpty(Name) ? job.Name : $"{job.Name} ({Name})"; var source = ProfileScheduleResource.CreateSource(selection); var duration = TimeSpan.FromMinutes(Duration); var noStartBefore = NoStartBefore; var start = FirstStart; // Check repetition var repeat = CreateRepeatPattern(); if (repeat == null) { // Only if not being recorded if (!noStartBefore.HasValue) { scheduler.Add(RecordingDefinition.Create(this, name, identifier, devices, source, start, duration)); } } else { // See if we have to adjust the start day if (noStartBefore.HasValue) { // Attach to the limit - actually we shift it a bit further assuming that we did have no large exception towards the past and the duration is moderate var startAfter = noStartBefore.Value.AddHours(12); var startAfterDay = startAfter.ToLocalTime().Date; // Localize the start time var startTime = start.ToLocalTime().TimeOfDay; // First adjust start = (startAfterDay + startTime).ToUniversalTime(); // One more day if (start < startAfter) { start = (startAfterDay.AddDays(1) + startTime).ToUniversalTime(); } } // Read the rest var exceptions = Exceptions.Select(e => e.ToPlanException(duration)).ToArray(); var endDay = LastDay.GetValueOrDefault(MaxMovableDay); // A bit more complex if (start.Date <= endDay.Date) { scheduler.Add(RecordingDefinition.Create(this, name, identifier, devices, source, start, duration, endDay, repeat), exceptions); } } }
/// <summary> /// Prüft, ob die Daten zur Aufzeichnung zulässig sind. /// </summary> /// <param name="job">Der zugehörige Auftrag.</param> /// <exception cref="InvalidJobDataException">Es wurde keine eindeutige Kennung angegeben.</exception> /// <exception cref="InvalidJobDataException">Die Daten der Aufzeichnung sind fehlerhaft.</exception> public void Validate(VCRJob job) { // Identifier if (!UniqueID.HasValue) { throw new InvalidJobDataException(Properties.Resources.BadUniqueID); } // Check for termination date if (LastDay.HasValue) { // Must be a date if (LastDay.Value != LastDay.Value.Date) { throw new InvalidJobDataException(Properties.Resources.LastDayNotDate); } if (FirstStart.Date > LastDay.Value.Date) { throw new InvalidJobDataException(Properties.Resources.EndsBeforeStart); } } // Duration if ((Duration < 1) || (Duration > 9999)) { throw new InvalidJobDataException(Properties.Resources.BadDuration); } // Repetition if (Days.HasValue) { if (0 != (~0x7f & (int)Days.Value)) { throw new InvalidJobDataException(Properties.Resources.BadRepeat); } } // Test the station if (Source != null) { // Match profile if (job != null) { if (job.Source != null) { if (!string.Equals(job.Source.ProfileName, Source.ProfileName, StringComparison.InvariantCultureIgnoreCase)) { throw new InvalidJobDataException(Properties.Resources.BadTVStation); } } } // Source if (!Source.Validate()) { throw new InvalidJobDataException(Properties.Resources.BadTVStation); } // Streams if (!Streams.Validate()) { throw new InvalidJobDataException(Properties.Resources.BadStreams); } } else if (Streams != null) { throw new InvalidJobDataException(Properties.Resources.BadStreams); } // Station if (!job.HasSource) { if (Source == null) { throw new InvalidJobDataException(Properties.Resources.NoTVStation); } } // Name if (!Name.IsValidName()) { throw new InvalidJobDataException(Properties.Resources.BadName); } }
/// <summary> /// Registriert diese Aufzeichnung in einer Planungsinstanz. /// </summary> /// <param name="scheduler">Die zu verwendende Planungsinstanz.</param> /// <param name="job">Der zugehörige Auftrag.</param> /// <param name="devices">Die Liste der Geräte, auf denen die Aufzeichnung ausgeführt werden darf.</param> /// <param name="findSource">Dient zum Prüfen einer Quelle.</param> /// <param name="disabled">Alle deaktivierten Aufträge.</param> /// <param name="context">Die aktuelle Planungsumgebung.</param> /// <exception cref="ArgumentNullException">Es wurden nicht alle Parameter angegeben.</exception> public void AddToScheduler( RecordingScheduler scheduler, VCRJob job, IScheduleResource[] devices, Func<SourceSelection, SourceSelection> findSource, Func<Guid, bool> disabled, PlanContext context ) { // Validate if (scheduler == null) throw new ArgumentNullException( nameof( scheduler ) ); if (job == null) throw new ArgumentNullException( nameof( job ) ); if (findSource == null) throw new ArgumentNullException( nameof( findSource ) ); // Let VCR.NET choose a profile to do the work if (job.AutomaticResourceSelection) devices = null; // Create the source selection var persistedSource = Source ?? job.Source; var selection = findSource( persistedSource ); // Station no longer available if (selection == null) if (persistedSource != null) selection = new SourceSelection { DisplayName = persistedSource.DisplayName, ProfileName = persistedSource.ProfileName, Location = persistedSource.Location, Group = persistedSource.Group, Source = new Station { TransportStream = persistedSource.Source?.TransportStream ?? 0, Network = persistedSource.Source?.Network ?? 0, Service = persistedSource.Source?.Service ?? 0, Name = persistedSource.DisplayName, }, }; // See if we are allowed to process var identifier = UniqueID.Value; if (disabled != null) if (disabled( identifier )) return; // Load all var name = string.IsNullOrEmpty( Name ) ? job.Name : $"{job.Name} ({Name})"; var source = ProfileScheduleResource.CreateSource( selection ); var duration = TimeSpan.FromMinutes( Duration ); var noStartBefore = NoStartBefore; var start = FirstStart; // Check repetition var repeat = CreateRepeatPattern(); if (repeat == null) { // Only if not being recorded if (!noStartBefore.HasValue) scheduler.Add( RecordingDefinition.Create( this, name, identifier, devices, source, start, duration ) ); } else { // See if we have to adjust the start day if (noStartBefore.HasValue) { // Attach to the limit - actually we shift it a bit further assuming that we did have no large exception towards the past and the duration is moderate var startAfter = noStartBefore.Value.AddHours( 12 ); var startAfterDay = startAfter.ToLocalTime().Date; // Localize the start time var startTime = start.ToLocalTime().TimeOfDay; // First adjust start = (startAfterDay + startTime).ToUniversalTime(); // One more day if (start < startAfter) start = (startAfterDay.AddDays( 1 ) + startTime).ToUniversalTime(); } // Read the rest var exceptions = Exceptions.Select( e => e.ToPlanException( duration ) ).ToArray(); var endDay = LastDay.GetValueOrDefault( MaxMovableDay ); // A bit more complex if (start.Date <= endDay.Date) scheduler.Add( RecordingDefinition.Create( this, name, identifier, devices, source, start, duration, endDay, repeat ), exceptions ); } }
/// <summary> /// Prüft, ob die Daten zur Aufzeichnung zulässig sind. /// </summary> /// <param name="job">Der zugehörige Auftrag.</param> /// <exception cref="InvalidJobDataException">Es wurde keine eindeutige Kennung angegeben.</exception> /// <exception cref="InvalidJobDataException">Die Daten der Aufzeichnung sind fehlerhaft.</exception> public void Validate( VCRJob job ) { // Identifier if (!UniqueID.HasValue) throw new InvalidJobDataException( Properties.Resources.BadUniqueID ); // Check for termination date if (LastDay.HasValue) { // Must be a date if (LastDay.Value != LastDay.Value.Date) throw new InvalidJobDataException( Properties.Resources.LastDayNotDate ); if (FirstStart.Date > LastDay.Value.Date) throw new InvalidJobDataException( Properties.Resources.EndsBeforeStart ); } // Duration if ((Duration < 1) || (Duration > 9999)) throw new InvalidJobDataException( Properties.Resources.BadDuration ); // Repetition if (Days.HasValue) if (0 != (~0x7f & (int) Days.Value)) throw new InvalidJobDataException( Properties.Resources.BadRepeat ); // Test the station if (Source != null) { // Match profile if (job != null) if (job.Source != null) if (!string.Equals( job.Source.ProfileName, Source.ProfileName, StringComparison.InvariantCultureIgnoreCase )) throw new InvalidJobDataException( Properties.Resources.BadTVStation ); // Source if (!Source.Validate()) throw new InvalidJobDataException( Properties.Resources.BadTVStation ); // Streams if (!Streams.Validate()) throw new InvalidJobDataException( Properties.Resources.BadStreams ); } else if (Streams != null) throw new InvalidJobDataException( Properties.Resources.BadStreams ); // Station if (!job.HasSource) if (Source == null) throw new InvalidJobDataException( Properties.Resources.NoTVStation ); // Name if (!Name.IsValidName()) throw new InvalidJobDataException( Properties.Resources.BadName ); }
/// <summary> /// Ermittelt eine Referenz für eine bestimmte Aufzeichung in einem Auftrag, so dass diese /// auch in einer URL verwendet werden kann. /// </summary> /// <param name="job">Ein Auftrag.</param> /// <param name="schedule">Eine Aufzeichnung.</param> /// <returns>Die eindeutige Referenz.</returns> public static string GetUniqueWebId( VCRJob job, VCRSchedule schedule ) { // Forward if (job == null) return "*"; else if (schedule == null) return string.Format( "*{0:N}", job.UniqueID.Value ); else return GetUniqueWebId( job.UniqueID.Value, schedule.UniqueID.Value ); }
/// <summary> /// Aktualisiert einen Auftrag oder legt einen Auftrag neu an. /// </summary> /// <param name="job">Der neue oder veränderte Auftrag.</param> /// <param name="scheduleIdentifier">Die eindeutige Kennung der veränderten Aufzeichnung.</param> public void Update( VCRJob job, Guid? scheduleIdentifier ) { // Report if (job != null) Tools.ExtendedLogging( "Updating Job {0}", job.UniqueID ); // Load default profile name job.SetProfile(); // Validate job.Validate( scheduleIdentifier ); // Cleanup schedules job.CleanupExceptions(); // Remove from archive - if job has been recovered job.Delete( ArchiveDirectory ); // Try to store to disk - actually this is inside the lock because the directory virtually is part of our map lock (m_Jobs) if (job.Save( JobDirectory ).GetValueOrDefault()) m_Jobs[job.UniqueID.Value] = job; else throw new ArgumentException( string.Format( Properties.Resources.SaveJobFailed, job.UniqueID ), "job" ); }
/// <summary> /// Löscht einen aktiven oder archivierten Auftrag. /// </summary> /// <param name="job">Der zu löschende Auftrag.</param> public void Delete( VCRJob job ) { // Check unique identifier if (!job.UniqueID.HasValue) throw new InvalidJobDataException( Properties.Resources.BadUniqueID ); // Report Tools.ExtendedLogging( "Deleting Job {0}", job.UniqueID ); // Must synchronize lock (m_Jobs) { // Load from the map var internalJob = this[job.UniqueID.Value]; // See if this is active if (internalJob != null) { // Delete it internalJob.Delete( JobDirectory ); // Remove from map m_Jobs.Remove( internalJob.UniqueID.Value ); // Save to file internalJob.Save( ArchiveDirectory ); } else { // Report Tools.ExtendedLogging( "Job not found in Active Directory - trying Archive" ); // Must be archived job.Delete( ArchiveDirectory ); } } }
/// <summary> /// Rekonstruiert einen Auftrag und eine Aufzeichnung aus einer Textdarstellung. /// </summary> /// <param name="id">Die Textdarstellung.</param> /// <param name="job">Der ermittelte Auftrag.</param> /// <returns>Die zugehörige Aufzeichnung im Auftrag.</returns> public static VCRSchedule ParseUniqueWebId( string id, out VCRJob job ) { // Read all Guid jobID, scheduleID; ParseUniqueWebId( id, out jobID, out scheduleID ); // Find the job job = VCRServer.FindJob( jobID ); // Report schedule if job exists if (job == null) return null; else return job[scheduleID]; }
/// <summary> /// Erstellt einen passenden Auftrag für die persistente Ablage. /// </summary> /// <param name="jobIdentifier">Die eindeutige Kennung des Auftrags.</param> /// <returns>Der zugehörige Auftrag.</returns> public VCRJob CreateJob( Guid jobIdentifier ) { // Create core var job = new VCRJob { AutomaticResourceSelection = !UseProfileForRecording, Directory = RecordingDirectory, UniqueID = jobIdentifier, Name = Name, }; // Check source var profile = Profile; if (string.IsNullOrEmpty( profile )) return job; // Get the name of the source var sourceName = Source; if (string.IsNullOrEmpty( sourceName )) { // Create profile reference job.Source = new SourceSelection { ProfileName = profile }; // Done return job; } // Locate the source job.Source = ServerRuntime.VCRServer.FindSource( profile, sourceName ); if (job.Source == null) return job; // Configure streams job.Streams = new StreamSelection(); // Set all - oder of audio settings is relevant, dolby MUST come last job.Streams.SetUsesAllAudio( AllLanguages ); job.Streams.SetUsesDolbyAudio( DolbyDigital ); job.Streams.SetUsesSubtitles( DVBSubtitles ); job.Streams.SetUsesVideotext( Videotext ); job.Streams.ProgramGuide = true; // Report return job; }
/// <summary> /// Erstellt die Beschreibung der Aufzeichnung für die persistente Ablage. /// </summary> /// <param name="scheduleIdentifier">Die eindeutige Kennung der Aufzeichnung.</param> /// <param name="job">Der zugehörige Auftrag.</param> /// <returns>Die gewünschte Beschreibung.</returns> public VCRSchedule CreateSchedule( Guid scheduleIdentifier, VCRJob job ) { // Create var schedule = new VCRSchedule { UniqueID = scheduleIdentifier, FirstStart = FirstStart, Days = RepeatPattern, Duration = Duration, LastDay = LastDay, Name = Name, }; // See if we have a source var sourceName = Source; if (string.IsNullOrEmpty( sourceName )) return schedule; // See if there is a profile var jobSource = job.Source; if (jobSource == null) return schedule; // Locate the source schedule.Source = ServerRuntime.VCRServer.FindSource( jobSource.ProfileName, sourceName ); if (schedule.Source == null) return schedule; // Configure streams schedule.Streams = new StreamSelection(); // Set all - oder of audio settings is relevant, dolby MUST come last schedule.Streams.SetUsesAllAudio( AllLanguages ); schedule.Streams.SetUsesDolbyAudio( DolbyDigital ); schedule.Streams.SetUsesSubtitles( DVBSubtitles ); schedule.Streams.SetUsesVideotext( Videotext ); schedule.Streams.ProgramGuide = true; // Report return schedule; }
/// <summary> /// Erstellt die Beschreibung der Aufzeichnung für die persistente Ablage. /// </summary> /// <param name="job">Der zugehörige Auftrag.</param> /// <returns>Die gewünschte Beschreibung.</returns> public VCRSchedule CreateSchedule( VCRJob job ) { // Forward return CreateSchedule( Guid.NewGuid(), job ); }
/// <summary> /// Erstellt eine Beschreibung zu dieser Aufzeichnung. /// </summary> /// <param name="schedule">Eine Aufzeichnung.</param> /// <param name="job">Der bereits vorhandene Auftrag.</param> /// <param name="guide">Ein Eintrag aus der Programmzeitschrift.</param> /// <returns>Die gewünschte Beschreibung.</returns> public static EditSchedule Create( VCRSchedule schedule, VCRJob job, ProgramGuideEntry guide ) { // None if (schedule == null) { // No hope if (guide == null) return null; // Calculate var start = guide.StartTime - TimeSpan.FromMinutes( UserProfileSettings.EPGPreTime ); var duration = checked( (int) (UserProfileSettings.EPGPreTime + (guide.Duration / 60) + UserProfileSettings.EPGPostTime) ); // Partial - we have a brand new job which is pre-initialized with the source if (job == null) return new EditSchedule { FirstStart = start, Duration = duration }; // Full monty - we have to overwrite the jobs settings since we are not the first schedule return new EditSchedule { Source = ServerRuntime.VCRServer.GetUniqueName( new SourceSelection { ProfileName = job.Source.ProfileName, Source = guide.Source } ), DVBSubtitles = UserProfileSettings.UseSubTitles, DolbyDigital = UserProfileSettings.UseAC3, AllLanguages = UserProfileSettings.UseMP2, Videotext = UserProfileSettings.UseTTX, Name = guide.Name.MakeValid(), Duration = duration, FirstStart = start, }; } // Consolidate exceptions schedule.CleanupExceptions(); // Optionen ermitteln var streams = schedule.Streams; var sourceName = ServerRuntime.VCRServer.GetUniqueName( schedule.Source ); // Create return new EditSchedule { Exceptions = schedule.Exceptions.Select( exception => PlanException.Create( exception, schedule ) ).ToArray(), LastDay = schedule.LastDay.GetValueOrDefault( VCRSchedule.MaxMovableDay ), DolbyDigital = streams.GetUsesDolbyAudio(), DVBSubtitles = streams.GetUsesSubtitles(), AllLanguages = streams.GetUsesAllAudio(), Videotext = streams.GetUsesVideotext(), FirstStart = schedule.FirstStart, RepeatPattern = schedule.Days, Duration = schedule.Duration, Name = schedule.Name, Source = sourceName, }; }