namespace TimelapseApi; using FlashCap; using FlashCap.Utilities; using FlashCap.Devices; using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; using Eto.Forms; using TimelapseApi.ClassExtensions; using Newtonsoft.Json; public class GuiData { public async Task Set() { if(Instance ==null) return; Api.natfs.api=Instance; var s= Instance._extSettings; ExtensionLoader.Data=this; s.Add(( ()=>{ return new TimelapseSettings(Instance); },"Settings",TimelapseExtension.Null )); foreach(var ext in ExtensionLoader.GetTimelapseExtensions()) { ext.Instance=Instance; Instance.Extensions.Add(ext); await ext._Create(); } Instance.SetPriority(); } public void FSChanged() { if(Instance != null) { if(Instance.Project != null) { Instance.Project=null; } } } public IEnumerable<(Func>,string,CancellationToken,Task> Export,string Text,TimelapseExtension Extension,FileFilter[] SaveDialogFilters)>? Export {get { if(Instance == null) return null; return Instance._export; }} public IEnumerable<(Func Dialog,string Text,TimelapseExtension Extension)>? ExtensionSettings {get {if(Instance == null) return null; return Instance._extSettings;}} public Api? Instance {get;set;} public List<(TimelapseFileSystem FileSystem,string Text,TimelapseExtension)>? FileSystems {get { if(Instance==null)return null; return Instance._fs;}} public TimelapseFileSystem? CurrentFileSystem {get {var fs=FileSystems; if(fs==null) return null; return fs[CurrentFSIndex].FileSystem;} } public IEnumerable<(Func ShareActionAsync,string Text,TimelapseExtension extension)>? Share {get {if(Instance==null) return null;return Instance._share;}} public int CurrentFSIndex {get;set;} } internal class PriorityCompare : IComparer<(Func, Task> Handler, string HandlerName, TimelapseExtension Extension, int Priority)> { public int Compare((Func, Task> Handler, string HandlerName, TimelapseExtension Extension, int Priority) x, (Func, Task> Handler, string HandlerName, TimelapseExtension Extension, int Priority) y) { return y.Priority.CompareTo(x.Priority); } } public class Api { internal void SetPriority() { _frameHandlers.Sort(new PriorityCompare()); } public bool HasCamera {get;set;} public TimelapseSettingsModel Model = LoadModel(); internal static string ModelLocation = GetInternalFile("config.json"); private static TimelapseSettingsModel LoadModel() { Directory.CreateDirectory(GetInternalFile("ExtensionBinaries")); Directory.CreateDirectory(GetInternalFile("ExtensionData")); if(File.Exists(ModelLocation)) { TimelapseSettingsModel? model= JsonConvert.DeserializeObject(File.ReadAllText(ModelLocation)); if(model != null) return model; } return new TimelapseSettingsModel(); } public void SaveModel() { Directory.CreateDirectory(GetInternalFile()); File.WriteAllText(ModelLocation, JsonConvert.SerializeObject(Model)); } private static string Get() { if(File.Exists("appdir.txt") ) return File.ReadAllText("appdir.txt"); else return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),"TimelapseNow"); } public static string GetInternalFile(params string[] paths) { if(paths.Length == 0) return Get(); return Path.Combine(Get(),Path.Combine(paths)); } public event EventHandler? ProjectClosed; public event EventHandler? ProjectOpened; public Api(GuiData data) { Gui=data; } private TimelapseProject? _proj=null; public TimelapseProject? Project {get {return _proj;} set { if(value != null) { if(_proj != null) { ProjectClosed?.Invoke(this,EventArgs.Empty); } _proj=value; ProjectOpened?.Invoke(this,EventArgs.Empty); }else{ ProjectClosed?.Invoke(this,EventArgs.Empty); _proj=value; } }} public event EventHandler? RecordingChanged; public event EventHandler? RealTimeChanged; bool _rec,_real; public bool Recording {get { if(Project == null) return false; return _rec;} set { _rec=value; RecordingChanged?.Invoke(this,EventArgs.Empty); }} public bool RealTime { get{if(Project == null) return false; return _real;} set{ _real =value; RealTimeChanged?.Invoke(this,EventArgs.Empty); }} public async Task RecordFrame(Image img) { if(Project !=null && Recording) { var interval = RealTime ? TimeSpan.FromSeconds(1) : (TimeSpan)Project.Interval; var now=DateTime.Now; if((Project.LastFrameTaken + (interval/10)) <= now) { Project.LastFrameTaken=now; await Project.Save(img); } } } public async Task SendFrame(Image img) { bool canRecordFrame=true; foreach(var item in _frameHandlers) { if(ExtensionAllowedToBeFrameHandler(item.Extension)) { if(item.Handler != null) { var res=await item.Handler(img); if(!res && ExtensionsCanBlockFrames(item.Extension)) canRecordFrame=false; } } } if(canRecordFrame) { await RecordFrame(img); } } internal bool ExtensionsCanBlockFrames(TimelapseExtension ext) { if(!Model.canBlockFrames) return false; return !Model.deniedBlockExtensions.Contains( ext.Id); } internal bool ExtensionAllowedToBeFrameHandler(TimelapseExtension ext) { if(!Model.canOverlayVideo) return false; return !Model.deniedOverlayExtensions.Contains( ext.Id); } private GuiData Gui; public int CurrentFileSystemIndex {get {return Gui.CurrentFSIndex;}} internal List<(TimelapseFileSystem FileSystem,string Text,TimelapseExtension Extension)> _fs = CreateFileSystemList(); internal static NativeFileSystem natfs=new NativeFileSystem(); internal List Extensions =new List(); private static List<(TimelapseFileSystem FileSystem,string Text,TimelapseExtension Extension)> CreateFileSystemList() { List<(TimelapseFileSystem FileSystem,string Text,TimelapseExtension Extension)> fs=new List<(TimelapseFileSystem FileSystem,string Text,TimelapseExtension Extension)>(); fs.Add((natfs,"Native",TimelapseExtension.Null)); return fs; } internal List<(Func,Task> Handler,string HandlerName, TimelapseExtension Extension,int Priority)> _frameHandlers=new List<(Func, Task> Handler,string HandlerName, TimelapseExtension Extension,int Priority)>(); internal List<(Func Dialog,string Text,TimelapseExtension Extension)> _extSettings= new List<(Func Dialog,string Text,TimelapseExtension Extension)>(); public async Task Export(Window w,int exportId) { if(Project == null) return null; var e=_export[exportId]; using(var sfd = new SaveFileDialog()) { foreach(var item in e.SaveDialogFilters) { sfd.Filters.Add(item); } if(sfd.ShowDialog(w) == DialogResult.Ok) { try{ using(var cts=new CancellationTokenSource()) { using(ExportForm frm=new ExportForm(Project,cts)) { frm.Show(); await e.Export(frm,sfd.FileName,cts.Token); } } return sfd.FileName; }catch(Exception ex) { _=ex; } } return null; } } internal List<(Func>,string,CancellationToken,Task> Export,string Text,TimelapseExtension Extension,FileFilter[] SaveDialogFilters)> _export=GetExports(); private static List<(Func>, string, CancellationToken, Task> Export, string Text, TimelapseExtension Extension, FileFilter[] SaveDialogFilters)> GetExports() { var exp=new List<(Func>,string,CancellationToken,Task> Export,string Text,TimelapseExtension Extension,FileFilter[] SaveDialogFilters)>(); Func>,string,CancellationToken,Task> callback=async(images,fname,token)=>{ using(var vfc=new VideoFileCreator(fname)) { foreach(var img in images) { await vfc.AddAsync(img); if(token.IsCancellationRequested) { break; } } } }; exp.Add((callback,"Export (FFmpeg)",TimelapseExtension.Null,new FileFilter[]{new FileFilter("MPEG-4",".mp4"),new FileFilter("MKV",".mkv")})); return exp; } internal List<(Func ShareActionAsync,string Text,TimelapseExtension Extension)> _share=new List<(Func ShareActionAsync, string Text,TimelapseExtension Extension)>(); }