using System.Diagnostics; using System.Reflection; using System.Text; namespace Shoko; [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] class ProtocolAttribute : Attribute { public string Protocol; public ProtocolAttribute(string proto) { Protocol = proto; } } class ProtoHandler { public Tab CurrentTab; public Uri URL; public string MediaType; public Stream Content; public string Status; public bool IsLoaded = false; protected long _loadedBytes = 0; public virtual long LoadedBytes => _loadedBytes; public virtual long TotalBytes => -1; public IEnumerable>> Headers; public IEnumerable> MediaTypeParams; public MediaHandler Media; public ProtoHandler() { } public ProtoHandler(Uri url) { URL = url; } public virtual Task Load() { Content = new MemoryStream(Encoding.UTF8.GetBytes("error: no handler for this scheme")); MediaType = "text/plain"; OnLoaded(); return Task.CompletedTask; } public async Task Download(Stream stream, Action progress) { var ret = new MemoryStream(); var buf = new byte[81920]; long totalBytes = 0; int bytesRead; while ((bytesRead = await stream.ReadAsync(buf, 0, buf.Length)) != 0) { await ret.WriteAsync(buf.AsMemory(0, bytesRead)); totalBytes += bytesRead; progress(totalBytes); } ret.Position = 0; return ret; } public async Task Download(Stream stream) { return await Download(stream, b=>_loadedBytes=b); } public virtual void OnLoaded() { Media = MediaHandler.GetHandler(this); IsLoaded = true; Media.Load(); } public virtual void Render() { Gui.Button("Open with external program", ()=>{ try { if(OperatingSystem.IsLinux()) Process.Start("xdg-open", URL.ToString()); else if(OperatingSystem.IsMacOS()) Process.Start("open", URL.ToString()); else if(OperatingSystem.IsWindows()) Process.Start(URL.ToString()); } catch{} }); } public virtual void MenuBar() { } /// /// Get the appropriate protocol handler for the URL /// /// /// public static ProtoHandler GetHandler(Uri url) { var proto = new UriBuilder(url).Scheme; var type = Assembly.GetAssembly(typeof(ProtoHandler)).GetTypes() .Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof(ProtoHandler)) && t.GetCustomAttributes().Any(x => x.Protocol == proto)) .SingleOrDefault(typeof(ProtoHandler)); return (ProtoHandler)Activator.CreateInstance(type, url); } public static ProtoHandler GetHandler(string url) { return GetHandler(new Uri(url)); } public static string[] SupportedProtos { get { return Assembly.GetAssembly(typeof(ProtoHandler)).GetTypes() .Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof(ProtoHandler))) .SelectMany(t => t.GetCustomAttributes().Select(x => x.Protocol)) .ToArray(); } } }