timelapsenow/TimelapseApi/Api.cs

277 lines
10 KiB
C#

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<IEnumerable<Image<Rgb24>>,string,CancellationToken,Task> Export,string Text,TimelapseExtension Extension,FileFilter[] SaveDialogFilters)>? Export {get {
if(Instance == null) return null;
return Instance._export;
}}
public IEnumerable<(Func<Dialog> 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<Window,string,Task> 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<Image<Rgb24>, Task<bool>> Handler, string HandlerName, TimelapseExtension Extension, int Priority)>
{
public int Compare((Func<Image<Rgb24>, Task<bool>> Handler, string HandlerName, TimelapseExtension Extension, int Priority) x, (Func<Image<Rgb24>, Task<bool>> Handler, string HandlerName, TimelapseExtension Extension, int Priority) y)
{
return x.Priority.CompareTo(y.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<TimelapseSettingsModel>(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<EventArgs>? ProjectClosed;
public event EventHandler<EventArgs>? 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<EventArgs>? RecordingChanged;
public event EventHandler<EventArgs>? 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<Rgb24> 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<Rgb24> 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<IDisposable> Extensions =new List<IDisposable>();
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<Image<Rgb24>,Task<bool>> Handler,string HandlerName, TimelapseExtension Extension,int Priority)> _frameHandlers=new List<(Func<Image<Rgb24>, Task<bool>> Handler,string HandlerName, TimelapseExtension Extension,int Priority)>();
internal List<(Func<Dialog> Dialog,string Text,TimelapseExtension Extension)> _extSettings= new List<(Func<Dialog> Dialog,string Text,TimelapseExtension Extension)>();
public async Task<string?> 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<IEnumerable<Image<Rgb24>>,string,CancellationToken,Task> Export,string Text,TimelapseExtension Extension,FileFilter[] SaveDialogFilters)> _export=GetExports();
private static List<(Func<IEnumerable<Image<Rgb24>>, string, CancellationToken, Task> Export, string Text, TimelapseExtension Extension, FileFilter[] SaveDialogFilters)> GetExports()
{
var exp=new List<(Func<IEnumerable<Image<Rgb24>>,string,CancellationToken,Task> Export,string Text,TimelapseExtension Extension,FileFilter[] SaveDialogFilters)>();
Func<IEnumerable<Image<Rgb24>>,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<Window,string,Task> ShareActionAsync,string Text,TimelapseExtension Extension)> _share=new List<(Func<Window,string, Task> ShareActionAsync, string Text,TimelapseExtension Extension)>();
}