shoko/Protocols/ProtoHandler.cs

124 lines
3.5 KiB
C#

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<KeyValuePair<string,IEnumerable<string>>> Headers;
public IEnumerable<KeyValuePair<string,string>> 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<MemoryStream> Download(Stream stream, Action<long> 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<MemoryStream> 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()
{
}
/// <summary>
/// Get the appropriate protocol handler for the URL
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
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<ProtocolAttribute>().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<ProtocolAttribute>().Select(x => x.Protocol))
.ToArray();
}
}
}