// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Data; namespace Spludlow.Video { /// /// Very simple PVR demo /// /// Call SubmitRecording() method, it will drop a file /// /// The "Run" method will pick up dropped files & start VLC recording for a specific duration when required. /// /// Ensure "Run" thead is running (line in Spludlow-Service.txt) /// Method VideoRecorder Spludlow Spludlow.Video.Recorder Run /// /// Make sure config set: "VideoRecorder.Directory" and have permissions to directory /// public class Recorder { private string RecordDirectory; private string SubmitDirectory; private string ScheduleFilename; private int TickMilliSeconds = 10 * 1000; public DataTable Schedule; public Recorder() { this.Load(false); } public Recorder(bool dontLoad) { this.Load(dontLoad); } public DataTable NewScheduleTable() { return Spludlow.Data.TextTable.ReadText(new string[] { "Number StartTime EndTime Filename Frequency ServiceId", "Int32* DateTime* DateTime String Int32 Int32", }); } public void Load(bool dontLoad) { this.RecordDirectory = Spludlow.Config.Get("VideoRecorder.Directory"); this.SubmitDirectory = this.RecordDirectory + @"\Submit"; this.ScheduleFilename = this.RecordDirectory + @"\Schedule.txt"; if (dontLoad == true) return; if (Directory.Exists(this.RecordDirectory) == false) Directory.CreateDirectory(this.RecordDirectory); if (Directory.Exists(this.SubmitDirectory) == false) Directory.CreateDirectory(this.SubmitDirectory); if (File.Exists(this.ScheduleFilename) == false) { this.Schedule = this.NewScheduleTable(); Spludlow.Data.TextTable.Write(this.ScheduleFilename, this.Schedule); } this.Schedule = Spludlow.Data.TextTable.ReadFile(this.ScheduleFilename); } public void SubmitRecording(DataRow channel, DataRow programme) { int channelNumber = (int)channel["Number"]; DateTime startTime = (DateTime)programme["StartDate"]; DateTime endTime = (DateTime)programme["EndDate"]; string channelName = (string)channel["Name"]; string programmeTitle = (string)programme["Title"]; int frequency = (int)channel["Frequency"]; int serviceId = (int)channel["ServiceId"]; this.SubmitRecording(channelNumber, startTime, endTime, channelName, programmeTitle, frequency, serviceId); } public void SubmitRecording(int channelNumber, DateTime startTime, DateTime endTime, string channelName, string programmeTitle, int frequency, int serviceId) { startTime = TruncateToMinutes(startTime); channelName = FixName(channelName); programmeTitle = FixName(programmeTitle); StringBuilder text = new StringBuilder(); text.Append(startTime.ToString("yyyy-MM-dd_HH-mm")); text.Append("_"); text.Append(channelNumber); text.Append("_"); text.Append(Spludlow.Io.Paths.LegalFileName(channelName)); text.Append("_"); text.Append(Spludlow.Io.Paths.LegalFileName(programmeTitle)); string filename = text.ToString(); DataTable table = this.NewScheduleTable(); table.Rows.Add(new object[] { channelNumber, startTime, endTime, filename, frequency, serviceId }); Spludlow.Data.TextTable.Write(this.SubmitDirectory + @"\" + filename + ".txt", table); } private static string FixName(string name) { name = name.Replace('\'', '_'); name = name.Replace('"', '_'); return name; } private System.Threading.EventWaitHandle WaitFinished = null; public void Run() { this.WaitFinished = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.ManualReset); Spludlow.Log.Report("VideoRecorder; Starting", new object[] { this.Schedule }); while (this.WaitFinished.WaitOne(this.TickMilliSeconds) == false) { int changes = 0; changes += this.CheckSubmit(); changes += this.CheckSchedule(); if (changes > 0) { Spludlow.Data.TextTable.Write(this.ScheduleFilename, this.Schedule); Spludlow.Log.Info("VideoRecorder; Changes:\t" + changes, new object[] { this.Schedule }); } } Spludlow.Log.Finish("VideoRecorder; Clean Exit", new object[] { this.Schedule }); } public void Stop() { this.WaitFinished.Set(); } private int CheckSubmit() { int changeCount = 0; foreach (string filename in Directory.GetFiles(this.SubmitDirectory, "*.txt")) { if (Spludlow.Io.Files.IsOpen(filename) == true) continue; DataTable importTable = Spludlow.Data.TextTable.ReadFile(filename); foreach (DataRow row in importTable.Rows) { this.Schedule.ImportRow(row); Spludlow.Log.Info("VideoRecorder; Import Recording:\t" + (string)row["Filename"]); ++changeCount; } File.Delete(filename); } return changeCount; } private int CheckSchedule() { DateTime nowTime = TruncateToMinutes(DateTime.Now); List removeList = new List(); foreach (DataRow row in this.Schedule.Rows) { DateTime startTime = (DateTime)row["StartTime"]; DateTime endTime = (DateTime)row["EndTime"]; if (nowTime >= startTime) { string filename = (string)row["Filename"]; int durationMinutes = (int)(endTime - nowTime).TotalMinutes; if (nowTime < endTime && durationMinutes > 0) { int frequency = (int)row["Frequency"]; int serviceId = (int)row["ServiceId"]; filename = this.RecordDirectory + @"\" + filename + ".ts"; Spludlow.VLC.StartRecording(filename, durationMinutes * 60, frequency, serviceId); Spludlow.Log.Report("VideoRecorder; Started Recording:\t" + filename); } else { Spludlow.Log.Warning("VideoRecorder; Missed Recording:\t" + filename); } removeList.Add(row); } } foreach (DataRow row in removeList) row.Delete(); this.Schedule.AcceptChanges(); return removeList.Count; } public static DateTime TruncateToMinutes(DateTime dateTime) { return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, 0); } } }