Merge branch 'refactoring' into migrate-to-c#
commit
f007befa54
|
@ -348,6 +348,11 @@ MigrationBackup/
|
||||||
|
|
||||||
# Ionide (cross platform F# VS Code tools) working folder
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
.ionide/
|
.ionide/
|
||||||
|
app/XDM/XDM.Win.Installer/ffmpeg-x86.exe
|
||||||
|
*.wixobj
|
||||||
|
app/XDM/XDM.Win.Installer/net4.7.2.wxs
|
||||||
|
app/XDM/XDM.Win.Installer/net4.7.2/
|
||||||
|
app/XDM/XDM.Win.Installer/*.msi
|
||||||
|
*.wixpdb
|
||||||
|
app/XDM/XDM.Win.Installer/*.exe
|
||||||
app/xdm-browser-monitor/extension-code.js
|
app/xdm-browser-monitor/extension-code.js
|
||||||
app/XDM/translation-generator/
|
|
||||||
app/XDM/XDM.Win.Installer/
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[*.cs]
|
||||||
|
|
||||||
|
# CS8618: Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||||
|
dotnet_diagnostic.CS8618.severity = silent
|
|
@ -1,25 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFrameworks>net3.5;net4.5;net4.7.2;net5.0</TargetFrameworks>
|
|
||||||
<Platforms>AnyCPU;x86</Platforms>
|
|
||||||
<LangVersion>9.0</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' != 'net5.0' ">
|
|
||||||
<ProjectReference Include="..\NetFX.Polyfill\NetFX.Polyfill.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\HttpServer\HttpServer.csproj" />
|
|
||||||
<ProjectReference Include="..\XDM_CoreFx\XDM.Core.csproj" />
|
|
||||||
<ProjectReference Include="..\TraceLog\TraceLog.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' != 'net3.5' ">
|
|
||||||
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
|
@ -1,297 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.IO.Pipes;
|
|
||||||
using System.IO;
|
|
||||||
using XDM.Core.Lib.Common;
|
|
||||||
using System.Threading;
|
|
||||||
#if NET35
|
|
||||||
using NetFX.Polyfill;
|
|
||||||
#else
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
#endif
|
|
||||||
using TraceLog;
|
|
||||||
|
|
||||||
namespace BrowserMonitoring
|
|
||||||
{
|
|
||||||
public class NativeMessagingHostHandler : IDisposable
|
|
||||||
{
|
|
||||||
private int MaxPipeInstance = 254;
|
|
||||||
private static readonly string PipeName = "XDM_Ipc_Browser_Monitoring_Pipe";
|
|
||||||
private List<NamedPipeServerStream> inPipes = new();
|
|
||||||
private Dictionary<NamedPipeServerStream, NamedPipeClientStream> inOutMap = new();
|
|
||||||
private readonly IApp app;
|
|
||||||
private static Mutex globalMutex;
|
|
||||||
private readonly BlockingCollection<byte[]> Messages = new();
|
|
||||||
private Thread WriterThread;
|
|
||||||
|
|
||||||
public static void EnsureSingleInstance(IApp app)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var mutex = Mutex.OpenExisting(@"Global\XDM_Active_Instance");
|
|
||||||
throw new InstanceAlreadyRunningException(@"XDM instance already running, Mutex exists 'Global\XDM_Active_Instance'");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Debug(ex, "Exception in NativeMessagingHostHandler ctor");
|
|
||||||
if (ex is InstanceAlreadyRunningException)
|
|
||||||
{
|
|
||||||
Log.Debug(ex, "Sending args to running instance");
|
|
||||||
|
|
||||||
if (app.Args != null && app.Args.Length > 0)
|
|
||||||
{
|
|
||||||
SendArgsToRunningInstance(app.Args);
|
|
||||||
Environment.Exit(0);
|
|
||||||
}
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
globalMutex = new Mutex(true, @"Global\XDM_Active_Instance");
|
|
||||||
}
|
|
||||||
|
|
||||||
public NativeMessagingHostHandler(IApp app)
|
|
||||||
{
|
|
||||||
this.app = app;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var mutex = Mutex.OpenExisting(@"Global\XDM_Active_Instance");
|
|
||||||
throw new InstanceAlreadyRunningException(@"XDM instance already running, Mutex exists 'Global\XDM_Active_Instance'");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Debug(ex, "Exception in NativeMessagingHostHandler ctor");
|
|
||||||
if (ex is InstanceAlreadyRunningException)
|
|
||||||
{
|
|
||||||
Log.Debug(ex, "Sending args to running instance");
|
|
||||||
|
|
||||||
if (app.Args != null && app.Args.Length > 0)
|
|
||||||
{
|
|
||||||
SendArgsToRunningInstance(app.Args);
|
|
||||||
Environment.Exit(0);
|
|
||||||
}
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
globalMutex = new Mutex(true, @"Global\XDM_Active_Instance");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BroadcastConfig()
|
|
||||||
{
|
|
||||||
var bytes = GetSyncBytes(app);
|
|
||||||
Messages.Add(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StartPipedChannel()
|
|
||||||
{
|
|
||||||
WriterThread = new Thread(() =>
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
//Log.Debug("Total messages to be sent to native host: " + Messages.Count);
|
|
||||||
var bytes = Messages.Take();
|
|
||||||
foreach (var key in inOutMap.Keys)
|
|
||||||
{
|
|
||||||
//Log.Debug("Sending message to native host");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var outpipe = inOutMap[key];
|
|
||||||
WriteMessage(outpipe, bytes);
|
|
||||||
//Log.Debug("Send message to native host successfully");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Debug(ex, "Send message to native host failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
WriterThread.Start();
|
|
||||||
new Thread(() =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (inPipes.Count == MaxPipeInstance)
|
|
||||||
{
|
|
||||||
Log.Debug("Max pipe count of " + MaxPipeInstance + " is reached");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var inPipe =
|
|
||||||
new NamedPipeServerStream(PipeName,
|
|
||||||
PipeDirection.In, NamedPipeServerStream.MaxAllowedServerInstances,
|
|
||||||
PipeTransmissionMode.Byte, PipeOptions.WriteThrough);
|
|
||||||
inPipes.Add(inPipe);
|
|
||||||
var first = true;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
Log.Debug("Waiting for native host pipe...");
|
|
||||||
inPipe.WaitForConnection();
|
|
||||||
Log.Debug("Pipe request received");
|
|
||||||
|
|
||||||
if (first)
|
|
||||||
{
|
|
||||||
Log.Debug("Creating one more additional pipe");
|
|
||||||
StartPipedChannel();
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ConsumePipe(inPipe);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
inPipe.Disconnect();
|
|
||||||
Log.Debug(e, "Error in message exchange");
|
|
||||||
}
|
|
||||||
Log.Debug("Terminated message exchange, will reuse the pipe");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Debug(ex, "Error in message exchange flow");
|
|
||||||
}
|
|
||||||
}).Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConsumePipe(NamedPipeServerStream inPipe)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Log.Debug("Initiate message handshake");
|
|
||||||
var clientPipeName = Encoding.UTF8.GetString(ReadMessageBytes(inPipe));
|
|
||||||
Log.Debug("Client pipe: " + clientPipeName);
|
|
||||||
if (clientPipeName.StartsWith("XDM-APP-"))
|
|
||||||
{
|
|
||||||
var command = ReadMessageBytes(inPipe);
|
|
||||||
var args = ArgsProcessor.ParseArgs(Encoding.UTF8.GetString(command).Split('\r'));
|
|
||||||
ArgsProcessor.Process(app, args);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var outPipe = new NamedPipeClientStream(".", clientPipeName, PipeDirection.Out);
|
|
||||||
outPipe.Connect();
|
|
||||||
SendConfig(outPipe);
|
|
||||||
inOutMap[inPipe] = outPipe;
|
|
||||||
Log.Debug("Message handshake completed");
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
var text = ReadMessageBytes(inPipe);
|
|
||||||
using var ms = new MemoryStream(text);
|
|
||||||
using var br = new BinaryReader(ms);
|
|
||||||
// Log.Debug("{Text}", text);
|
|
||||||
var envelop = RawBrowserMessageEnvelop.Deserialize(br);
|
|
||||||
BrowserMessageHandler.Handle(app, envelop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
NamedPipeClientStream? op = null;
|
|
||||||
lock (this)
|
|
||||||
{
|
|
||||||
if (inOutMap.TryGetValue(inPipe, out op))
|
|
||||||
{
|
|
||||||
inOutMap.Remove(inPipe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
op?.Close();
|
|
||||||
op?.Dispose();
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendConfig(Stream pipe)
|
|
||||||
{
|
|
||||||
var bytes = GetSyncBytes(app);
|
|
||||||
WriteMessage(pipe, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ReadFully(Stream stream, byte[] buf, int bytesToRead)
|
|
||||||
{
|
|
||||||
var rem = bytesToRead;
|
|
||||||
var index = 0;
|
|
||||||
while (rem > 0)
|
|
||||||
{
|
|
||||||
var c = stream.Read(buf, index, rem);
|
|
||||||
if (c == 0) throw new IOException("Unexpected EOF");
|
|
||||||
index += c;
|
|
||||||
rem -= c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] ReadMessageBytes(Stream pipe)
|
|
||||||
{
|
|
||||||
var b4 = new byte[4];
|
|
||||||
ReadFully(pipe, b4, 4);
|
|
||||||
var syncLength = BitConverter.ToInt32(b4, 0);
|
|
||||||
var bytes = new byte[syncLength];
|
|
||||||
ReadFully(pipe, bytes, syncLength);
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void WriteMessage(Stream pipe, string message)
|
|
||||||
{
|
|
||||||
var msgBytes = Encoding.UTF8.GetBytes(message);
|
|
||||||
WriteMessage(pipe, msgBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void WriteMessage(Stream pipe, byte[] msgBytes)
|
|
||||||
{
|
|
||||||
var bytes = BitConverter.GetBytes(msgBytes.Length);
|
|
||||||
pipe.Write(bytes, 0, bytes.Length);
|
|
||||||
pipe.Write(msgBytes, 0, msgBytes.Length);
|
|
||||||
pipe.Flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
foreach (var pipe in inPipes)
|
|
||||||
{
|
|
||||||
try { pipe.Disconnect(); } catch { }
|
|
||||||
try { pipe.Dispose(); } catch { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] GetSyncBytes(IApp app)
|
|
||||||
{
|
|
||||||
var msg = new SyncMessage()
|
|
||||||
{
|
|
||||||
Enabled = Config.Instance.IsBrowserMonitoringEnabled,
|
|
||||||
BlockedHosts = Config.Instance.BlockedHosts,
|
|
||||||
VideoUrls = new string[0],
|
|
||||||
FileExts = Config.Instance.FileExtensions,
|
|
||||||
VidExts = Config.Instance.VideoExtensions,
|
|
||||||
VidList = app.GetVideoList(false).Select(a => new VideoItem
|
|
||||||
{
|
|
||||||
Id = a.ID,
|
|
||||||
Text = a.File,
|
|
||||||
Info = a.DisplayName
|
|
||||||
}).ToList(),
|
|
||||||
MimeList = new string[] { "video", "audio", "mpegurl", "f4m", "m3u8", "dash" },
|
|
||||||
BlockedMimeList = new string[] { "text/javascript", "application/javascript", "text/css", "text/html" },
|
|
||||||
VideoUrlsWithPostReq = new string[] { "ubei/v1/player?key=", "ubei/v1/next?key=" }
|
|
||||||
};
|
|
||||||
return msg.Serialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SendArgsToRunningInstance(string[] args)
|
|
||||||
{
|
|
||||||
using var clientPipe =
|
|
||||||
new NamedPipeClientStream(".", PipeName, PipeDirection.Out);
|
|
||||||
clientPipe.Connect();
|
|
||||||
WriteMessage(clientPipe, $"XDM-APP-${Guid.NewGuid()}");
|
|
||||||
WriteMessage(clientPipe, string.Join("\r", args));
|
|
||||||
clientPipe.Flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class InstanceAlreadyRunningException : Exception
|
|
||||||
{
|
|
||||||
public InstanceAlreadyRunningException(string message) : base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using XDM.Core.Lib.Util;
|
|
||||||
|
|
||||||
|
|
||||||
namespace BrowserMonitoring
|
|
||||||
{
|
|
||||||
public struct RawBrowserMessageEnvelop
|
|
||||||
{
|
|
||||||
public string MessageType { get; set; }
|
|
||||||
|
|
||||||
public RawBrowserMessage Message { get; set; }
|
|
||||||
|
|
||||||
public string[] VideoIds { get; set; }
|
|
||||||
|
|
||||||
public void Serialize(BinaryWriter w)
|
|
||||||
{
|
|
||||||
RawBrowserMessageEnvelopSerializerV1.Serialize(this, w);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RawBrowserMessageEnvelop Deserialize(BinaryReader r)
|
|
||||||
{
|
|
||||||
var version = r.ReadInt32();
|
|
||||||
if (version == 1)
|
|
||||||
{
|
|
||||||
return RawBrowserMessageEnvelopSerializerV1.Deserialize(r);
|
|
||||||
}
|
|
||||||
throw new InvalidDataException($"Version ${version} not supported.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static class RawBrowserMessageEnvelopSerializerV1
|
|
||||||
{
|
|
||||||
public static void Serialize(RawBrowserMessageEnvelop e, BinaryWriter w)
|
|
||||||
{
|
|
||||||
w.Write(1);
|
|
||||||
w.Write(e.MessageType);
|
|
||||||
w.Write(e.Message != null);
|
|
||||||
if (e.Message != null)
|
|
||||||
{
|
|
||||||
w.Write(e.Message.Url ?? string.Empty);
|
|
||||||
w.Write(e.Message.File ?? string.Empty);
|
|
||||||
w.Write(e.Message.Method ?? string.Empty);
|
|
||||||
w.Write(e.Message.RequestBody ?? string.Empty);
|
|
||||||
Helpers.WriteStateHeaders(e.Message.RequestHeaders, w);
|
|
||||||
Helpers.WriteStateHeaders(e.Message.ResponseHeaders, w);
|
|
||||||
Helpers.WriteStateCookies(e.Message.Cookies, w);
|
|
||||||
}
|
|
||||||
var count = e.VideoIds?.Length ?? 0;
|
|
||||||
w.Write(count);
|
|
||||||
if (e.VideoIds != null && e.VideoIds.Length > 0)
|
|
||||||
{
|
|
||||||
foreach (var item in e.VideoIds)
|
|
||||||
{
|
|
||||||
w.Write(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RawBrowserMessageEnvelop Deserialize(BinaryReader r)
|
|
||||||
{
|
|
||||||
var e = new RawBrowserMessageEnvelop { };
|
|
||||||
e.MessageType = Helpers.ReadString(r);
|
|
||||||
if (r.ReadBoolean())
|
|
||||||
{
|
|
||||||
e.Message = new();
|
|
||||||
e.Message.Url = Helpers.ReadString(r);
|
|
||||||
e.Message.File = Helpers.ReadString(r);
|
|
||||||
e.Message.Method = Helpers.ReadString(r);
|
|
||||||
e.Message.RequestBody = Helpers.ReadString(r);
|
|
||||||
Helpers.ReadStateHeaders(r, out Dictionary<string, List<string>> dict1);
|
|
||||||
Helpers.ReadStateHeaders(r, out Dictionary<string, List<string>> dict2);
|
|
||||||
Helpers.ReadStateCookies(r, out Dictionary<string, string> dict3);
|
|
||||||
e.Message.RequestHeaders = dict1;
|
|
||||||
e.Message.ResponseHeaders = dict2;
|
|
||||||
e.Message.Cookies = dict3;
|
|
||||||
}
|
|
||||||
var count = r.ReadInt32();
|
|
||||||
e.VideoIds = new string[count];
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
e.VideoIds[i] = r.ReadString();
|
|
||||||
}
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net4.5.2</TargetFramework>
|
|
||||||
<LangVersion>9.0</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="System.AppContext" Version="4.3.0" />
|
|
||||||
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
|
||||||
<PackageReference Include="System.Diagnostics.Tools" Version="4.3.0" />
|
|
||||||
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
|
|
||||||
<PackageReference Include="System.Runtime.Numerics" Version="4.3.0" />
|
|
||||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
|
@ -1,7 +0,0 @@
|
||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
namespace System.Runtime.CompilerServices
|
|
||||||
{
|
|
||||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
||||||
internal class IsExternalInit { }
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
// Licensed to the .NET Foundation under one or more agreements.
|
|
||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
|
||||||
|
|
||||||
namespace System.Numerics.Hashing
|
|
||||||
{
|
|
||||||
internal static class HashHelpers
|
|
||||||
{
|
|
||||||
public static int Combine(int h1, int h2)
|
|
||||||
{
|
|
||||||
// RyuJIT optimizes this to use the ROL instruction
|
|
||||||
// Related GitHub pull request: https://github.com/dotnet/coreclr/pull/1830
|
|
||||||
uint rol5 = ((uint)h1 << 5) | ((uint)h1 >> 27);
|
|
||||||
return ((int)rol5 + h1) ^ h2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,157 +0,0 @@
|
||||||
// Licensed to the .NET Foundation under one or more agreements.
|
|
||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
|
||||||
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace System
|
|
||||||
{
|
|
||||||
/// <summary>Represent a type can be used to index a collection either from the start or the end.</summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Index is used by the C# compiler to support the new index syntax
|
|
||||||
/// <code>
|
|
||||||
/// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ;
|
|
||||||
/// int lastElement = someArray[^1]; // lastElement = 5
|
|
||||||
/// </code>
|
|
||||||
/// </remarks>
|
|
||||||
public readonly struct Index : IEquatable<Index>
|
|
||||||
{
|
|
||||||
private readonly int _value;
|
|
||||||
|
|
||||||
/// <summary>Construct an Index using a value and indicating if the index is from the start or from the end.</summary>
|
|
||||||
/// <param name="value">The index value. it has to be zero or positive number.</param>
|
|
||||||
/// <param name="fromEnd">Indicating if the index is from the start or from the end.</param>
|
|
||||||
/// <remarks>
|
|
||||||
/// If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element.
|
|
||||||
/// </remarks>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public Index(int value, bool fromEnd = false)
|
|
||||||
{
|
|
||||||
if (value < 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException("ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException();");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fromEnd)
|
|
||||||
_value = ~value;
|
|
||||||
else
|
|
||||||
_value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The following private constructors mainly created for perf reason to avoid the checks
|
|
||||||
private Index(int value)
|
|
||||||
{
|
|
||||||
_value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Create an Index pointing at first element.</summary>
|
|
||||||
public static Index Start => new Index(0);
|
|
||||||
|
|
||||||
/// <summary>Create an Index pointing at beyond last element.</summary>
|
|
||||||
public static Index End => new Index(~0);
|
|
||||||
|
|
||||||
/// <summary>Create an Index from the start at the position indicated by the value.</summary>
|
|
||||||
/// <param name="value">The index value from the start.</param>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static Index FromStart(int value)
|
|
||||||
{
|
|
||||||
if (value < 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException("ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Index(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Create an Index from the end at the position indicated by the value.</summary>
|
|
||||||
/// <param name="value">The index value from the end.</param>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static Index FromEnd(int value)
|
|
||||||
{
|
|
||||||
if (value < 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException("ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException();");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Index(~value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Returns the index value.</summary>
|
|
||||||
public int Value
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_value < 0)
|
|
||||||
return ~_value;
|
|
||||||
else
|
|
||||||
return _value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Indicates whether the index is from the start or the end.</summary>
|
|
||||||
public bool IsFromEnd => _value < 0;
|
|
||||||
|
|
||||||
/// <summary>Calculate the offset from the start using the giving collection length.</summary>
|
|
||||||
/// <param name="length">The length of the collection that the Index will be used with. length has to be a positive value</param>
|
|
||||||
/// <remarks>
|
|
||||||
/// For performance reason, we don't validate the input length parameter and the returned offset value against negative values.
|
|
||||||
/// we don't validate either the returned offset is greater than the input length.
|
|
||||||
/// It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and
|
|
||||||
/// then used to index a collection will get out of range exception which will be same affect as the validation.
|
|
||||||
/// </remarks>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public int GetOffset(int length)
|
|
||||||
{
|
|
||||||
int offset = _value;
|
|
||||||
if (IsFromEnd)
|
|
||||||
{
|
|
||||||
// offset = length - (~value)
|
|
||||||
// offset = length + (~(~value) + 1)
|
|
||||||
// offset = length + value + 1
|
|
||||||
|
|
||||||
offset += length + 1;
|
|
||||||
}
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Indicates whether the current Index object is equal to another object of the same type.</summary>
|
|
||||||
/// <param name="value">An object to compare with this object</param>
|
|
||||||
public override bool Equals([NotNullWhen(true)] object? value) => value is Index && _value == ((Index)value)._value;
|
|
||||||
|
|
||||||
/// <summary>Indicates whether the current Index object is equal to another Index object.</summary>
|
|
||||||
/// <param name="other">An object to compare with this object</param>
|
|
||||||
public bool Equals(Index other) => _value == other._value;
|
|
||||||
|
|
||||||
/// <summary>Returns the hash code for this instance.</summary>
|
|
||||||
public override int GetHashCode() => _value;
|
|
||||||
|
|
||||||
/// <summary>Converts integer number to an Index.</summary>
|
|
||||||
public static implicit operator Index(int value) => FromStart(value);
|
|
||||||
|
|
||||||
/// <summary>Converts the value of the current Index object to its equivalent string representation.</summary>
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
if (IsFromEnd)
|
|
||||||
return ToStringFromEnd();
|
|
||||||
|
|
||||||
return ((uint)Value).ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private string ToStringFromEnd()
|
|
||||||
{
|
|
||||||
#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
|
|
||||||
Span<char> span = stackalloc char[11]; // 1 for ^ and 10 for longest possible uint value
|
|
||||||
bool formatted = ((uint)Value).TryFormat(span.Slice(1), out int charsWritten);
|
|
||||||
Debug.Assert(formatted);
|
|
||||||
span[0] = '^';
|
|
||||||
return new string(span.Slice(0, charsWritten + 1));
|
|
||||||
#else
|
|
||||||
return '^' + Value.ToString();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
// Licensed to the .NET Foundation under one or more agreements.
|
|
||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
|
||||||
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace System.Collections.Generic
|
|
||||||
{
|
|
||||||
// Provides the Create factory method for KeyValuePair<TKey, TValue>.
|
|
||||||
public static class KeyValuePair
|
|
||||||
{
|
|
||||||
// Creates a new KeyValuePair<TKey, TValue> from the given values.
|
|
||||||
public static KeyValuePair<TKey, TValue> Create<TKey, TValue>(TKey key, TValue value)
|
|
||||||
{
|
|
||||||
return new KeyValuePair<TKey, TValue>(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used by KeyValuePair.ToString to reduce generic code
|
|
||||||
/// </summary>
|
|
||||||
internal static string PairToString(object? key, object? value)
|
|
||||||
{
|
|
||||||
var s = new StringBuilder();
|
|
||||||
|
|
||||||
s.Append('[');
|
|
||||||
|
|
||||||
if (key != null)
|
|
||||||
{
|
|
||||||
s.Append(key.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Append(", ");
|
|
||||||
|
|
||||||
if (value != null)
|
|
||||||
{
|
|
||||||
s.Append(value.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Append(']');
|
|
||||||
|
|
||||||
return s.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//// A KeyValuePair holds a key and a value from a dictionary.
|
|
||||||
//// It is used by the IEnumerable<T> implementation for both IDictionary<TKey, TValue>
|
|
||||||
//// and IReadOnlyDictionary<TKey, TValue>.
|
|
||||||
//[Serializable]
|
|
||||||
//[System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
|
|
||||||
//public readonly struct KeyValuePair<TKey, TValue>
|
|
||||||
//{
|
|
||||||
// [DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
||||||
// private readonly TKey key; // Do not rename (binary serialization)
|
|
||||||
// [DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
||||||
// private readonly TValue value; // Do not rename (binary serialization)
|
|
||||||
|
|
||||||
// public KeyValuePair(TKey key, TValue value)
|
|
||||||
// {
|
|
||||||
// this.key = key;
|
|
||||||
// this.value = value;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public TKey Key => key;
|
|
||||||
|
|
||||||
// public TValue Value => value;
|
|
||||||
|
|
||||||
// public override string ToString()
|
|
||||||
// {
|
|
||||||
// return KeyValuePair.PairToString(Key, Value);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// [EditorBrowsable(EditorBrowsableState.Never)]
|
|
||||||
// public void Deconstruct(out TKey key, out TValue value)
|
|
||||||
// {
|
|
||||||
// key = Key;
|
|
||||||
// value = Value;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
#if NETFRAMEWORK || NETSTANDARD2_0
|
|
||||||
|
|
||||||
namespace System.Diagnostics.CodeAnalysis
|
|
||||||
{
|
|
||||||
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the corresponding type allows it.</summary>
|
|
||||||
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
|
|
||||||
public sealed class NotNullWhenAttribute : Attribute
|
|
||||||
{
|
|
||||||
/// <summary>Initializes the attribute with the specified return value condition.</summary>
|
|
||||||
/// <param name="returnValue">
|
|
||||||
/// The return value condition. If the method returns this value, the associated parameter will not be null.
|
|
||||||
/// </param>
|
|
||||||
public NotNullWhenAttribute(bool returnValue)
|
|
||||||
{
|
|
||||||
ReturnValue = returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Gets the return value condition.</summary>
|
|
||||||
public bool ReturnValue { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -1,72 +0,0 @@
|
||||||
// Licensed to the .NET Foundation under one or more agreements.
|
|
||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
|
||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace System.Runtime.InteropServices
|
|
||||||
{
|
|
||||||
public readonly struct OSPlatform : IEquatable<OSPlatform>
|
|
||||||
{
|
|
||||||
public static OSPlatform FreeBSD { get; } = new OSPlatform("FREEBSD");
|
|
||||||
|
|
||||||
public static OSPlatform Linux { get; } = new OSPlatform("LINUX");
|
|
||||||
|
|
||||||
public static OSPlatform OSX { get; } = new OSPlatform("OSX");
|
|
||||||
|
|
||||||
public static OSPlatform Windows { get; } = new OSPlatform("WINDOWS");
|
|
||||||
|
|
||||||
internal string Name { get; }
|
|
||||||
|
|
||||||
private OSPlatform(string osPlatform)
|
|
||||||
{
|
|
||||||
if (osPlatform == null) throw new ArgumentNullException(nameof(osPlatform));
|
|
||||||
if (osPlatform.Length == 0) throw new ArgumentException("SR.Argument_EmptyValue", nameof(osPlatform));
|
|
||||||
|
|
||||||
Name = osPlatform;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new OSPlatform instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>If you plan to call this method frequently, please consider caching its result.</remarks>
|
|
||||||
public static OSPlatform Create(string osPlatform)
|
|
||||||
{
|
|
||||||
return new OSPlatform(osPlatform);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(OSPlatform other)
|
|
||||||
{
|
|
||||||
return Equals(other.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool Equals(string? other)
|
|
||||||
{
|
|
||||||
return string.Equals(Name, other, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
|
||||||
{
|
|
||||||
return obj is OSPlatform osPlatform && Equals(osPlatform);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return Name == null ? 0 : StringComparer.OrdinalIgnoreCase.GetHashCode(Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Name ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(OSPlatform left, OSPlatform right)
|
|
||||||
{
|
|
||||||
return left.Equals(right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(OSPlatform left, OSPlatform right)
|
|
||||||
{
|
|
||||||
return !(left == right);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace CoreFx.Polyfill
|
|
||||||
{
|
|
||||||
public static class ProcessStartInfoHelper
|
|
||||||
{
|
|
||||||
public static string ArgumentListToArgsString(IList<string> arguments)
|
|
||||||
{
|
|
||||||
var stringBuilder = new StringBuilder();
|
|
||||||
foreach (var argument in arguments)
|
|
||||||
{
|
|
||||||
AppendArgument(stringBuilder, argument);
|
|
||||||
}
|
|
||||||
return stringBuilder.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AppendArgument(StringBuilder stringBuilder, string argument)
|
|
||||||
{
|
|
||||||
if (stringBuilder.Length != 0)
|
|
||||||
{
|
|
||||||
stringBuilder.Append(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parsing rules for non-argv[0] arguments:
|
|
||||||
// - Backslash is a normal character except followed by a quote.
|
|
||||||
// - 2N backslashes followed by a quote ==> N literal backslashes followed by unescaped quote
|
|
||||||
// - 2N+1 backslashes followed by a quote ==> N literal backslashes followed by a literal quote
|
|
||||||
// - Parsing stops at first whitespace outside of quoted region.
|
|
||||||
// - (post 2008 rule): A closing quote followed by another quote ==> literal quote, and parsing remains in quoting mode.
|
|
||||||
if (argument.Length != 0 && ContainsNoWhitespaceOrQuotes(argument))
|
|
||||||
{
|
|
||||||
// Simple case - no quoting or changes needed.
|
|
||||||
stringBuilder.Append(argument);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stringBuilder.Append(Quote);
|
|
||||||
int idx = 0;
|
|
||||||
while (idx < argument.Length)
|
|
||||||
{
|
|
||||||
char c = argument[idx++];
|
|
||||||
if (c == Backslash)
|
|
||||||
{
|
|
||||||
int numBackSlash = 1;
|
|
||||||
while (idx < argument.Length && argument[idx] == Backslash)
|
|
||||||
{
|
|
||||||
idx++;
|
|
||||||
numBackSlash++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idx == argument.Length)
|
|
||||||
{
|
|
||||||
// We'll emit an end quote after this so must double the number of backslashes.
|
|
||||||
stringBuilder.Append(Backslash, numBackSlash * 2);
|
|
||||||
}
|
|
||||||
else if (argument[idx] == Quote)
|
|
||||||
{
|
|
||||||
// Backslashes will be followed by a quote. Must double the number of backslashes.
|
|
||||||
stringBuilder.Append(Backslash, numBackSlash * 2 + 1);
|
|
||||||
stringBuilder.Append(Quote);
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Backslash will not be followed by a quote, so emit as normal characters.
|
|
||||||
stringBuilder.Append(Backslash, numBackSlash);
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == Quote)
|
|
||||||
{
|
|
||||||
// Escape the quote so it appears as a literal. This also guarantees that we won't end up generating a closing quote followed
|
|
||||||
// by another quote (which parses differently pre-2008 vs. post-2008.)
|
|
||||||
stringBuilder.Append(Backslash);
|
|
||||||
stringBuilder.Append(Quote);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
stringBuilder.Append(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
stringBuilder.Append(Quote);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool ContainsNoWhitespaceOrQuotes(string s)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < s.Length; i++)
|
|
||||||
{
|
|
||||||
char c = s[i];
|
|
||||||
if (char.IsWhiteSpace(c) || c == Quote)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private const char Quote = '\"';
|
|
||||||
private const char Backslash = '\\';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,133 +0,0 @@
|
||||||
// Licensed to the .NET Foundation under one or more agreements.
|
|
||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
|
||||||
|
|
||||||
using System.Collections;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Numerics.Hashing;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace System
|
|
||||||
{
|
|
||||||
/// <summary>Represent a range has start and end indexes.</summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Range is used by the C# compiler to support the range syntax.
|
|
||||||
/// <code>
|
|
||||||
/// int[] someArray = new int[5] { 1, 2, 3, 4, 5 };
|
|
||||||
/// int[] subArray1 = someArray[0..2]; // { 1, 2 }
|
|
||||||
/// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 }
|
|
||||||
/// </code>
|
|
||||||
/// </remarks>
|
|
||||||
public readonly struct Range : IEquatable<Range>
|
|
||||||
{
|
|
||||||
/// <summary>Represent the inclusive start index of the Range.</summary>
|
|
||||||
public Index Start { get; }
|
|
||||||
|
|
||||||
/// <summary>Represent the exclusive end index of the Range.</summary>
|
|
||||||
public Index End { get; }
|
|
||||||
|
|
||||||
/// <summary>Construct a Range object using the start and end indexes.</summary>
|
|
||||||
/// <param name="start">Represent the inclusive start index of the range.</param>
|
|
||||||
/// <param name="end">Represent the exclusive end index of the range.</param>
|
|
||||||
public Range(Index start, Index end)
|
|
||||||
{
|
|
||||||
Start = start;
|
|
||||||
End = end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Indicates whether the current Range object is equal to another object of the same type.</summary>
|
|
||||||
/// <param name="value">An object to compare with this object</param>
|
|
||||||
public override bool Equals([NotNullWhen(true)] object? value) =>
|
|
||||||
value is Range r &&
|
|
||||||
r.Start.Equals(Start) &&
|
|
||||||
r.End.Equals(End);
|
|
||||||
|
|
||||||
/// <summary>Indicates whether the current Range object is equal to another Range object.</summary>
|
|
||||||
/// <param name="other">An object to compare with this object</param>
|
|
||||||
public bool Equals(Range other) => other.Start.Equals(Start) && other.End.Equals(End);
|
|
||||||
|
|
||||||
/// <summary>Returns the hash code for this instance.</summary>
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
|
|
||||||
return HashCode.Combine(Start.GetHashCode(), End.GetHashCode());
|
|
||||||
#else
|
|
||||||
return HashHelpers.Combine(Start.GetHashCode(), End.GetHashCode());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Converts the value of the current Range object to its equivalent string representation.</summary>
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
#if (!NETSTANDARD2_0 && !NETFRAMEWORK)
|
|
||||||
Span<char> span = stackalloc char[2 + (2 * 11)]; // 2 for "..", then for each index 1 for '^' and 10 for longest possible uint
|
|
||||||
int pos = 0;
|
|
||||||
|
|
||||||
if (Start.IsFromEnd)
|
|
||||||
{
|
|
||||||
span[0] = '^';
|
|
||||||
pos = 1;
|
|
||||||
}
|
|
||||||
bool formatted = ((uint)Start.Value).TryFormat(span.Slice(pos), out int charsWritten);
|
|
||||||
Debug.Assert(formatted);
|
|
||||||
pos += charsWritten;
|
|
||||||
|
|
||||||
span[pos++] = '.';
|
|
||||||
span[pos++] = '.';
|
|
||||||
|
|
||||||
if (End.IsFromEnd)
|
|
||||||
{
|
|
||||||
span[pos++] = '^';
|
|
||||||
}
|
|
||||||
formatted = ((uint)End.Value).TryFormat(span.Slice(pos), out charsWritten);
|
|
||||||
Debug.Assert(formatted);
|
|
||||||
pos += charsWritten;
|
|
||||||
|
|
||||||
return new string(span.Slice(0, pos));
|
|
||||||
#else
|
|
||||||
return Start.ToString() + ".." + End.ToString();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Create a Range object starting from start index to the end of the collection.</summary>
|
|
||||||
public static Range StartAt(Index start) => new Range(start, Index.End);
|
|
||||||
|
|
||||||
/// <summary>Create a Range object starting from first element in the collection to the end Index.</summary>
|
|
||||||
public static Range EndAt(Index end) => new Range(Index.Start, end);
|
|
||||||
|
|
||||||
/// <summary>Create a Range object starting from first element to the end.</summary>
|
|
||||||
public static Range All => new Range(Index.Start, Index.End);
|
|
||||||
|
|
||||||
/// <summary>Calculate the start offset and length of range object using a collection length.</summary>
|
|
||||||
/// <param name="length">The length of the collection that the range will be used with. length has to be a positive value.</param>
|
|
||||||
/// <remarks>
|
|
||||||
/// For performance reason, we don't validate the input length parameter against negative values.
|
|
||||||
/// It is expected Range will be used with collections which always have non negative length/count.
|
|
||||||
/// We validate the range is inside the length scope though.
|
|
||||||
/// </remarks>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public (int Offset, int Length) GetOffsetAndLength(int length)
|
|
||||||
{
|
|
||||||
int start;
|
|
||||||
Index startIndex = Start;
|
|
||||||
if (startIndex.IsFromEnd)
|
|
||||||
start = length - startIndex.Value;
|
|
||||||
else
|
|
||||||
start = startIndex.Value;
|
|
||||||
|
|
||||||
int end;
|
|
||||||
Index endIndex = End;
|
|
||||||
if (endIndex.IsFromEnd)
|
|
||||||
end = length - endIndex.Value;
|
|
||||||
else
|
|
||||||
end = endIndex.Value;
|
|
||||||
|
|
||||||
if ((uint)end > (uint)length || (uint)start > (uint)end)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException("ExceptionArgument.length");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (start, end - start);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
// Licensed to the .NET Foundation under one or more agreements.
|
|
||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
|
||||||
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace System.Runtime.InteropServices
|
|
||||||
{
|
|
||||||
public static partial class RuntimeInformation
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates whether the current application is running on the specified platform.
|
|
||||||
/// </summary>
|
|
||||||
public static bool IsOSPlatform(OSPlatform osPlatform) => IsOSPlatform(osPlatform.Name);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates whether the current application is running on the specified platform.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="platform">Case-insensitive platform name. Examples: Browser, Linux, FreeBSD, Android, iOS, macOS, tvOS, watchOS, Windows.</param>
|
|
||||||
public static bool IsOSPlatform(string platform)
|
|
||||||
{
|
|
||||||
if (platform == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(platform));
|
|
||||||
}
|
|
||||||
return platform.Equals("WINDOWS", StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,138 +0,0 @@
|
||||||
//// Licensed to the .NET Foundation under one or more agreements.
|
|
||||||
//// The .NET Foundation licenses this file to you under the MIT license.
|
|
||||||
|
|
||||||
//#nullable enable
|
|
||||||
//using System.Resources;
|
|
||||||
|
|
||||||
//namespace System
|
|
||||||
//{
|
|
||||||
// internal static partial class SR
|
|
||||||
// {
|
|
||||||
// private static readonly bool s_usingResourceKeys = false;
|
|
||||||
|
|
||||||
// // This method is used to decide if we need to append the exception message parameters to the message when calling SR.Format.
|
|
||||||
// // by default it returns the value of System.Resources.UseSystemResourceKeys AppContext switch or false if not specified.
|
|
||||||
// // Native code generators can replace the value this returns based on user input at the time of native code generation.
|
|
||||||
// // The Linker is also capable of replacing the value of this method when the application is being trimmed.
|
|
||||||
// private static bool UsingResourceKeys() => s_usingResourceKeys;
|
|
||||||
|
|
||||||
// internal static string GetResourceString(string resourceKey)
|
|
||||||
// {
|
|
||||||
// if (UsingResourceKeys())
|
|
||||||
// {
|
|
||||||
// return resourceKey;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// string? resourceString = null;
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// resourceString =
|
|
||||||
//#if SYSTEM_PRIVATE_CORELIB
|
|
||||||
// InternalGetResourceString(resourceKey);
|
|
||||||
//#else
|
|
||||||
// ResourceManager.GetString(resourceKey);
|
|
||||||
//#endif
|
|
||||||
// }
|
|
||||||
// catch (MissingManifestResourceException) { }
|
|
||||||
|
|
||||||
// return resourceString!; // only null if missing resources
|
|
||||||
// }
|
|
||||||
|
|
||||||
// internal static string GetResourceString(string resourceKey, string defaultString)
|
|
||||||
// {
|
|
||||||
// string resourceString = GetResourceString(resourceKey);
|
|
||||||
|
|
||||||
// return resourceKey == resourceString || resourceString == null ? defaultString : resourceString;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// internal static string Format(string resourceFormat, object? p1)
|
|
||||||
// {
|
|
||||||
// if (UsingResourceKeys())
|
|
||||||
// {
|
|
||||||
// return string.Join(", ", resourceFormat, p1);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return string.Format(resourceFormat, p1);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// internal static string Format(string resourceFormat, object? p1, object? p2)
|
|
||||||
// {
|
|
||||||
// if (UsingResourceKeys())
|
|
||||||
// {
|
|
||||||
// return string.Join(", ", resourceFormat, p1, p2);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return string.Format(resourceFormat, p1, p2);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// internal static string Format(string resourceFormat, object? p1, object? p2, object? p3)
|
|
||||||
// {
|
|
||||||
// if (UsingResourceKeys())
|
|
||||||
// {
|
|
||||||
// return string.Join(", ", resourceFormat, p1, p2, p3);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return string.Format(resourceFormat, p1, p2, p3);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// internal static string Format(string resourceFormat, params object?[]? args)
|
|
||||||
// {
|
|
||||||
// if (args != null)
|
|
||||||
// {
|
|
||||||
// if (UsingResourceKeys())
|
|
||||||
// {
|
|
||||||
// return resourceFormat + ", " + string.Join(", ", args);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return string.Format(resourceFormat, args);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return resourceFormat;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// internal static string Format(IFormatProvider? provider, string resourceFormat, object? p1)
|
|
||||||
// {
|
|
||||||
// if (UsingResourceKeys())
|
|
||||||
// {
|
|
||||||
// return string.Join(", ", resourceFormat, p1);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return string.Format(provider, resourceFormat, p1);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// internal static string Format(IFormatProvider? provider, string resourceFormat, object? p1, object? p2)
|
|
||||||
// {
|
|
||||||
// if (UsingResourceKeys())
|
|
||||||
// {
|
|
||||||
// return string.Join(", ", resourceFormat, p1, p2);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return string.Format(provider, resourceFormat, p1, p2);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// internal static string Format(IFormatProvider? provider, string resourceFormat, object? p1, object? p2, object? p3)
|
|
||||||
// {
|
|
||||||
// if (UsingResourceKeys())
|
|
||||||
// {
|
|
||||||
// return string.Join(", ", resourceFormat, p1, p2, p3);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return string.Format(provider, resourceFormat, p1, p2, p3);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// internal static string Format(IFormatProvider? provider, string resourceFormat, params object?[]? args)
|
|
||||||
// {
|
|
||||||
// if (args != null)
|
|
||||||
// {
|
|
||||||
// if (UsingResourceKeys())
|
|
||||||
// {
|
|
||||||
// return resourceFormat + ", " + string.Join(", ", args);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return string.Format(provider, resourceFormat, args);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return resourceFormat;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
|
@ -1,60 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace System
|
|
||||||
{
|
|
||||||
public static class ShimCompatExtensions
|
|
||||||
{
|
|
||||||
|
|
||||||
public static string Join<T>(char separator, IEnumerable<T> values)
|
|
||||||
{
|
|
||||||
return string.Join(separator.ToString(), values);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ContainsKey<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key)
|
|
||||||
{
|
|
||||||
return dict.TryGetValue(key, out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ContainsKey(this GroupCollection dict, string key)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return dict[key] != null;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TValue? GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key)
|
|
||||||
{
|
|
||||||
return GetValueOrDefault(dict, key, default(TValue)!);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value for a given key if a matching key exists in the dictionary.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The key to search for.</param>
|
|
||||||
/// <param name="defaultValue">The default value to return if no matching key is found in the dictionary.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// The value for the key, or <paramref name="defaultValue"/> if no matching key was found.
|
|
||||||
/// </returns>
|
|
||||||
public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue defaultValue)
|
|
||||||
{
|
|
||||||
|
|
||||||
TValue value;
|
|
||||||
if (dict.TryGetValue(key, out value!))
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace CoreFx.Polyfill
|
|
||||||
{
|
|
||||||
public static class StringExtensions
|
|
||||||
{
|
|
||||||
public static string[] Split(this string str)
|
|
||||||
{
|
|
||||||
return str.Split(str.ToCharArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,2 @@
|
||||||
|
Clone https://github.com/m-ab-s/media-autobuild_suite
|
||||||
|
Put ffmpeg_options.txt inside build folder
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Lines starting with this character are ignored
|
||||||
|
# To override some options specifically for the shared build, create a ffmpeg_options_shared.txt file.
|
||||||
|
|
||||||
|
# Basic built-in options, can be removed if you delete "--disable-autodetect"
|
||||||
|
--disable-everything
|
||||||
|
--disable-network
|
||||||
|
--disable-autodetect
|
||||||
|
--enable-small
|
||||||
|
--enable-demuxer=mov,mp4,m4a,3gp,3g2,mj2,matroska,webm,mpegts
|
||||||
|
--enable-libmp3lame
|
||||||
|
--enable-encoder=libmp3lame
|
||||||
|
--enable-muxer=mp4,matroska,mpegts,mp3
|
||||||
|
--enable-protocol=file,srt,concat
|
||||||
|
--enable-bsfs
|
||||||
|
--enable-filter=acopy,concat,copy
|
||||||
|
--enable-decoder=vorbis,opus,aac,ac3
|
|
@ -1,14 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFrameworks>net3.5;net4.5;net4.7.2;net5.0</TargetFrameworks>
|
|
||||||
<LangVersion>9.0</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' != 'net5.0' ">
|
|
||||||
<ProjectReference Include="..\NetFX.Polyfill\NetFX.Polyfill.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\TraceLog\TraceLog.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
|
@ -1,9 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFrameworks>net3.5;net4.5;net4.7.2;net5.0</TargetFrameworks>
|
|
||||||
<LangVersion>9.0</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
|
@ -1,208 +0,0 @@
|
||||||
// Licensed to the .NET Foundation under one or more agreements.
|
|
||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
Interop
|
|
||||||
{
|
|
||||||
class Libraries
|
|
||||||
{
|
|
||||||
public const string WinHttp = "winhttp.dll";
|
|
||||||
}
|
|
||||||
internal static partial class WinHttp
|
|
||||||
{
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
public static extern SafeWinHttpHandle WinHttpOpen(
|
|
||||||
IntPtr userAgent,
|
|
||||||
uint accessType,
|
|
||||||
string? proxyName,
|
|
||||||
string? proxyBypass, int flags);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpCloseHandle(
|
|
||||||
IntPtr handle);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
public static extern SafeWinHttpHandle WinHttpConnect(
|
|
||||||
SafeWinHttpHandle sessionHandle,
|
|
||||||
string serverName,
|
|
||||||
ushort serverPort,
|
|
||||||
uint reserved);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
public static extern SafeWinHttpHandle WinHttpOpenRequest(
|
|
||||||
SafeWinHttpHandle connectHandle,
|
|
||||||
string verb,
|
|
||||||
string objectName,
|
|
||||||
string? version,
|
|
||||||
string referrer,
|
|
||||||
string acceptTypes,
|
|
||||||
uint flags);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpAddRequestHeaders(
|
|
||||||
SafeWinHttpHandle requestHandle,
|
|
||||||
#pragma warning disable CA1838 // Uses pooled StringBuilder
|
|
||||||
[In] StringBuilder headers,
|
|
||||||
#pragma warning restore CA1838
|
|
||||||
uint headersLength,
|
|
||||||
uint modifiers);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpAddRequestHeaders(
|
|
||||||
SafeWinHttpHandle requestHandle,
|
|
||||||
string headers,
|
|
||||||
uint headersLength,
|
|
||||||
uint modifiers);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpSendRequest(
|
|
||||||
SafeWinHttpHandle requestHandle,
|
|
||||||
IntPtr headers,
|
|
||||||
uint headersLength,
|
|
||||||
IntPtr optional,
|
|
||||||
uint optionalLength,
|
|
||||||
uint totalLength,
|
|
||||||
IntPtr context);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpReceiveResponse(
|
|
||||||
SafeWinHttpHandle requestHandle,
|
|
||||||
IntPtr reserved);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpQueryDataAvailable(
|
|
||||||
SafeWinHttpHandle requestHandle,
|
|
||||||
IntPtr parameterIgnoredAndShouldBeNullForAsync);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpReadData(
|
|
||||||
SafeWinHttpHandle requestHandle,
|
|
||||||
IntPtr buffer,
|
|
||||||
uint bufferSize,
|
|
||||||
IntPtr parameterIgnoredAndShouldBeNullForAsync);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpQueryHeaders(
|
|
||||||
SafeWinHttpHandle requestHandle,
|
|
||||||
uint infoLevel,
|
|
||||||
string name,
|
|
||||||
IntPtr buffer,
|
|
||||||
ref uint bufferLength,
|
|
||||||
ref uint index);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpQueryHeaders(
|
|
||||||
SafeWinHttpHandle requestHandle,
|
|
||||||
uint infoLevel,
|
|
||||||
string name,
|
|
||||||
ref uint number,
|
|
||||||
ref uint bufferLength,
|
|
||||||
IntPtr index);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpQueryOption(
|
|
||||||
SafeWinHttpHandle handle,
|
|
||||||
uint option,
|
|
||||||
ref IntPtr buffer,
|
|
||||||
ref uint bufferSize);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpQueryOption(
|
|
||||||
SafeWinHttpHandle handle,
|
|
||||||
uint option,
|
|
||||||
IntPtr buffer,
|
|
||||||
ref uint bufferSize);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpQueryOption(
|
|
||||||
SafeWinHttpHandle handle,
|
|
||||||
uint option,
|
|
||||||
ref uint buffer,
|
|
||||||
ref uint bufferSize);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpWriteData(
|
|
||||||
SafeWinHttpHandle requestHandle,
|
|
||||||
IntPtr buffer,
|
|
||||||
uint bufferSize,
|
|
||||||
IntPtr parameterIgnoredAndShouldBeNullForAsync);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpSetOption(
|
|
||||||
SafeWinHttpHandle handle,
|
|
||||||
uint option,
|
|
||||||
ref uint optionData,
|
|
||||||
uint optionLength = sizeof(uint));
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpSetOption(
|
|
||||||
SafeWinHttpHandle handle,
|
|
||||||
uint option,
|
|
||||||
IntPtr optionData,
|
|
||||||
uint optionLength);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpSetCredentials(
|
|
||||||
SafeWinHttpHandle requestHandle,
|
|
||||||
uint authTargets,
|
|
||||||
uint authScheme,
|
|
||||||
string? userName,
|
|
||||||
string? password,
|
|
||||||
IntPtr reserved);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpQueryAuthSchemes(
|
|
||||||
SafeWinHttpHandle requestHandle,
|
|
||||||
out uint supportedSchemes,
|
|
||||||
out uint firstScheme,
|
|
||||||
out uint authTarget);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpSetTimeouts(
|
|
||||||
SafeWinHttpHandle handle,
|
|
||||||
int resolveTimeout,
|
|
||||||
int connectTimeout,
|
|
||||||
int sendTimeout,
|
|
||||||
int receiveTimeout);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpGetIEProxyConfigForCurrentUser(
|
|
||||||
out WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public static extern bool WinHttpGetProxyForUrl(
|
|
||||||
SafeWinHttpHandle? sessionHandle, string url,
|
|
||||||
ref WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions,
|
|
||||||
out WINHTTP_PROXY_INFO proxyInfo);
|
|
||||||
|
|
||||||
[DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
||||||
public static extern IntPtr WinHttpSetStatusCallback(
|
|
||||||
SafeWinHttpHandle handle,
|
|
||||||
WINHTTP_STATUS_CALLBACK callback,
|
|
||||||
uint notificationFlags,
|
|
||||||
IntPtr reserved);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -53,7 +53,7 @@ ND_ADDRESS=Address
|
||||||
ND_FILE=File
|
ND_FILE=File
|
||||||
LBL_SAVE_IN=Save in
|
LBL_SAVE_IN=Save in
|
||||||
ND_IGNORE_URL=Do not capture download from this address
|
ND_IGNORE_URL=Do not capture download from this address
|
||||||
ND_MORE=MORE...
|
ND_MORE=More...
|
||||||
ND_DOWNLOAD_LATER=Download Later
|
ND_DOWNLOAD_LATER=Download Later
|
||||||
ND_DOWNLOAD_NOW=Download Now
|
ND_DOWNLOAD_NOW=Download Now
|
||||||
ND_AUTO_CAT=Automatically select based on file type
|
ND_AUTO_CAT=Automatically select based on file type
|
||||||
|
@ -84,8 +84,8 @@ MSG_DOWNLOAD_FFMPEG=Download FFmpeg?
|
||||||
LBL_QUEUE_OPT3=Do not use queue
|
LBL_QUEUE_OPT3=Do not use queue
|
||||||
VID_PASTE_URL=Please paste video URL link here
|
VID_PASTE_URL=Please paste video URL link here
|
||||||
SETTINGS_ADV=Advanced settings
|
SETTINGS_ADV=Advanced settings
|
||||||
VID_CHK=Check/Uncheck All
|
VID_CHK=Select all
|
||||||
O_VID_FMT=Formats
|
O_VID_FMT=Format
|
||||||
BAT_PATTERN=Pattern
|
BAT_PATTERN=Pattern
|
||||||
BAT_LINKS=Links
|
BAT_LINKS=Links
|
||||||
BAT_SELECT_ITEMS=Select items to download
|
BAT_SELECT_ITEMS=Select items to download
|
||||||
|
@ -126,8 +126,8 @@ SETTINGS_GENERAL=General settings
|
||||||
SETTINGS_NETWORK=Network settings
|
SETTINGS_NETWORK=Network settings
|
||||||
SETTINGS_CRED=Password manager
|
SETTINGS_CRED=Password manager
|
||||||
SETTINGS_ADV=Advanced settings
|
SETTINGS_ADV=Advanced settings
|
||||||
DESC_MONITORING_1=Please select browsers to monitor and make sure, browser addon is installed and enabled in respective browsers
|
DESC_MONITORING_1=Please make sure XDM extension is installed in your browser. To install the browser extension, please click on the buttons below or copy paste the link directly in the browser
|
||||||
DESC_OTHER_BROWSERS=XDM can also be integrated into other Chromium based browser (SRWare Iron etc) or Mozilla based (Icewasel, Waterfox etc) browsers using below links
|
DESC_OTHER_BROWSERS=XDM can also be integrated into other Chromium based browser (Vivaldi, Brave browser etc) or Mozilla based (Icewasel, Waterfox etc) browsers using below links
|
||||||
DESC_CHROME=Chromium based browsers
|
DESC_CHROME=Chromium based browsers
|
||||||
DESC_MOZ=Firefox based browsers
|
DESC_MOZ=Firefox based browsers
|
||||||
DESC_FILETYPES=XDM will automatically take over downloads from browser for below file types
|
DESC_FILETYPES=XDM will automatically take over downloads from browser for below file types
|
||||||
|
@ -152,7 +152,7 @@ SETTINGS_FOLDER=Default download folder
|
||||||
SETTINGS_DARK_THEME=Use dark theme if possible (Require app restart)
|
SETTINGS_DARK_THEME=Use dark theme if possible (Require app restart)
|
||||||
SETTINGS_CAT_ADD=Add
|
SETTINGS_CAT_ADD=Add
|
||||||
SETTINGS_CAT_EDIT=Edit
|
SETTINGS_CAT_EDIT=Edit
|
||||||
DESC_NET1=Connection timeout
|
DESC_NET1=Connection timeout in seconds
|
||||||
DESC_NET2=Segments per download
|
DESC_NET2=Segments per download
|
||||||
NET_MAX_RETRY=Maximum retry limit
|
NET_MAX_RETRY=Maximum retry limit
|
||||||
NET_SYSTEM_PROXY=Use system proxy settings
|
NET_SYSTEM_PROXY=Use system proxy settings
|
||||||
|
@ -189,4 +189,31 @@ MSG_NO_UPDATE=No updates available/already upto date
|
||||||
MSG_UPDATED=Update successfull
|
MSG_UPDATED=Update successfull
|
||||||
MSG_ALREADY_RUNNING=An old version of XDM is already running
|
MSG_ALREADY_RUNNING=An old version of XDM is already running
|
||||||
MSG_BROWSER_LAUNCH_FAILED=Unable to launch
|
MSG_BROWSER_LAUNCH_FAILED=Unable to launch
|
||||||
MSG_NATIVE_HOST_FAILED=Error installing native host
|
MSG_NATIVE_HOST_FAILED=Error installing native host
|
||||||
|
MSG_DONT_SHOW_AGAIN=Don't show this again
|
||||||
|
MSG_NO_USERNAME=User name required
|
||||||
|
MSG_REF_LINK_MSG=New download link is accepted
|
||||||
|
MSG_CATEGORY=Category
|
||||||
|
MSG_CAT_NAME_MISSING=Category name required
|
||||||
|
MSG_CAT_FILE_TYPES_MISSING=File types required
|
||||||
|
MSG_CAT_FOLDER_MISSING=Download folder required
|
||||||
|
MSG_HOST_NAME_MISSING=Host name required
|
||||||
|
MSG_QUALITY=Quality
|
||||||
|
MSG_MP3=Mp3 Audio
|
||||||
|
MSG_TIME=Time
|
||||||
|
STAT_ASSEMBLING=Assembling
|
||||||
|
STAT_WAITING=Waiting
|
||||||
|
MSG_UPDATE_AVAILABLE=Update(s) available
|
||||||
|
MSG_RESTORE=Restore Window
|
||||||
|
MSG_DOUBLE_CLICK_ACTION=Double click on download item
|
||||||
|
MSG_OPEN_FILE=Open file
|
||||||
|
MSG_FALLBACK_UA=User agent to be used if download is added manually
|
||||||
|
MSG_SAVE_AS_MP3=Save as MP3
|
||||||
|
MSG_VID_WIKI_TEXT=If you are interested in downloading streaming video from the browser using XDM
|
||||||
|
MSG_VID_WIKI_LINK=Please click here
|
||||||
|
NO_REFRESH_LINK=Link refresh is not available for this download
|
||||||
|
MSG_NO_VIDEO=No video found. However there might be other ways to download the video. Please click "Learn More" button for details.
|
||||||
|
MSG_VIDEO_DOWNLOAD_HELP=Learn More
|
||||||
|
MSG_READ_BROWSER_COOKIE=Read cookies from browser
|
||||||
|
MSG_SELECT_FOLDER=Select
|
||||||
|
MSG_IMPORT_DONE=Import complete
|
|
@ -0,0 +1,25 @@
|
||||||
|
English=English.txt
|
||||||
|
Arabic (العربية)=Arabic.txt
|
||||||
|
Chinese simplified (简体中文)=Chinese simplified.txt
|
||||||
|
Chinese Traditional (繁體中文)=Chinese Traditional.txt
|
||||||
|
Czech=Czech.txt
|
||||||
|
Farsi - Persian (فارسی)=Farsi-Persian.txt
|
||||||
|
French(Français)=French.txt
|
||||||
|
German(Deutsch)=German.txt
|
||||||
|
Hungarian=Hungarian.txt
|
||||||
|
Indonesian (Bahasa Indonesia)=Indonesian.txt
|
||||||
|
Italian=Italian.txt
|
||||||
|
Korea (한국어)=Korea.txt
|
||||||
|
Malayalam=Malayalam.txt
|
||||||
|
Nepali=Nepali.txt
|
||||||
|
Polish=Polish.txt
|
||||||
|
Portuguese Brazil (Português (Brasil))=Portuguese Brazil.txt
|
||||||
|
Romanian (ROMÂNĂ)=Romanian.txt
|
||||||
|
Russian(Rусский)=Russian.txt
|
||||||
|
Serbian - Latin (Srpski (latinica))=Serbian - Latin.txt
|
||||||
|
Serbian Cyrillic (Српски (ћирилица))=Serbian Cyrillic.txt
|
||||||
|
Spanish (Español)=Spanish.txt
|
||||||
|
Traditional Chinese - Taiwan (繁體中文(台灣))=Traditional Chinese - Taiwan.txt
|
||||||
|
Turkish(Türkçe)=Turkish.txt
|
||||||
|
Ukrainian(Українська)=Ukrainian.txt
|
||||||
|
Vietnamese=Vietnamese.txt
|
|
@ -1,40 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFrameworks>net3.5;net4.5;net4.7.2;net5.0</TargetFrameworks>
|
|
||||||
<Platforms>AnyCPU;x86</Platforms>
|
|
||||||
<LangVersion>9.0</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!--<ItemGroup Condition=" '$(TargetFramework)' == 'net4.5.2' ">
|
|
||||||
<ProjectReference Include="..\CoreFx.Polyfill\CoreFx.Polyfill.csproj" />
|
|
||||||
</ItemGroup>-->
|
|
||||||
|
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' != 'net5.0' ">
|
|
||||||
<ProjectReference Include="..\NetFX.Polyfill\NetFX.Polyfill.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net4.5'">
|
|
||||||
<PackageReference Include="System.ValueTuple">
|
|
||||||
<Version>4.5.0</Version>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net4.7.2'">
|
|
||||||
<PackageReference Include="System.ValueTuple">
|
|
||||||
<Version>4.5.0</Version>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
|
|
||||||
<PackageReference Include="System.ValueTuple">
|
|
||||||
<Version>4.5.0</Version>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
|
@ -1,12 +1,17 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net5.0;net4.5.2</TargetFrameworks>
|
<TargetFrameworks>net5.0;</TargetFrameworks>
|
||||||
<Platforms>AnyCPU;x86</Platforms>
|
<Platforms>AnyCPU;x86;x64</Platforms>
|
||||||
<LangVersion>9.0</LangVersion>
|
<LangVersion>9.0</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net5.0|x86'">
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
<DebugSymbols>false</DebugSymbols>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
|
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
|
||||||
|
@ -14,8 +19,4 @@
|
||||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net4.5.2' ">
|
|
||||||
<ProjectReference Include="..\CoreFx.Polyfill\CoreFx.Polyfill.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
#if NET35
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace NetFX.Polyfill2
|
||||||
|
{
|
||||||
|
public class BlockingCollection<T>
|
||||||
|
{
|
||||||
|
private object _queueLock = new();
|
||||||
|
private Queue<T> _queue = new();
|
||||||
|
private AutoResetEvent _objectAvailableEvent = new(false);
|
||||||
|
|
||||||
|
public T Take()
|
||||||
|
{
|
||||||
|
lock (_queueLock)
|
||||||
|
{
|
||||||
|
if (_queue.Count > 0)
|
||||||
|
return _queue.Dequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
_objectAvailableEvent.WaitOne();
|
||||||
|
|
||||||
|
return Take();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(T obj)
|
||||||
|
{
|
||||||
|
lock (_queueLock)
|
||||||
|
{
|
||||||
|
_queue.Enqueue(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
_objectAvailableEvent.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -2,30 +2,27 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFrameworks>net3.5;net4.5;net4.7.2;net5.0</TargetFrameworks>
|
<TargetFrameworks>net3.5;net4.7.2;net6.0</TargetFrameworks>
|
||||||
<InvariantGlobalization>true</InvariantGlobalization>
|
<InvariantGlobalization>true</InvariantGlobalization>
|
||||||
<LangVersion>9.0</LangVersion>
|
<LangVersion>9.0</LangVersion>
|
||||||
<Platforms>AnyCPU;x86</Platforms>
|
<Nullable>enable</Nullable>
|
||||||
<!--<PublishTrimmed>true</PublishTrimmed>
|
<Platforms>x86;x64</Platforms>
|
||||||
|
<AssemblyName>xdm-messaging-host</AssemblyName>
|
||||||
|
<PublishTrimmed>true</PublishTrimmed>
|
||||||
<TrimMode>Link</TrimMode>
|
<TrimMode>Link</TrimMode>
|
||||||
<Platforms>AnyCPU;x86</Platforms>
|
<!--<Platforms>AnyCPU;x86</Platforms>-->
|
||||||
<RuntimeIdentifier>win-x86</RuntimeIdentifier>-->
|
<!--<RuntimeIdentifiers>win-x86;linux-x64</RuntimeIdentifiers>-->
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' != 'net5.0' ">
|
<!--<ItemGroup Condition=" '$(TargetFramework)' != 'net5.0' or '$(TargetFramework)' != 'net6.0' ">
|
||||||
<ProjectReference Include="..\NetFX.Polyfill\NetFX.Polyfill.csproj" />
|
<ProjectReference Include="..\NetFX.Polyfill\NetFX.Polyfill.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>-->
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<PropertyGroup Condition=" '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net4.7.2' or '$(TargetFramework)' == 'net4.5' ">
|
||||||
<ProjectReference Include="..\BrowserMonitor\BrowserMonitor.csproj" />
|
|
||||||
<ProjectReference Include="..\TraceLog\TraceLog.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'net5.0' or '$(TargetFramework)' == 'net4.7.2' or '$(TargetFramework)' == 'net4.5' ">
|
|
||||||
<AppConfig>App.PostDotNet4.config</AppConfig>
|
<AppConfig>App.PostDotNet4.config</AppConfig>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@ -33,4 +30,11 @@
|
||||||
<AppConfig>App.PreDotNet4.config</AppConfig>
|
<AppConfig>App.PreDotNet4.config</AppConfig>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net4.7.2|x86'">
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
<DebugSymbols>false</DebugSymbols>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Import Project="..\XDM.Messaging\XDM.Messaging.projitems" Label="Shared" />
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -6,11 +6,11 @@ using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using BrowserMonitoring;
|
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
|
using BrowserMonitoring;
|
||||||
|
|
||||||
#if NET35
|
#if NET35
|
||||||
using NetFX.Polyfill;
|
using NetFX.Polyfill2;
|
||||||
#else
|
#else
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
#endif
|
#endif
|
||||||
|
@ -19,44 +19,66 @@ namespace NativeHost
|
||||||
{
|
{
|
||||||
public class NativeMessagingHostApp
|
public class NativeMessagingHostApp
|
||||||
{
|
{
|
||||||
|
static bool isFirefox = true;
|
||||||
static BlockingCollection<byte[]> receivedBrowserMessages = new();
|
static BlockingCollection<byte[]> receivedBrowserMessages = new();
|
||||||
static BlockingCollection<byte[]> queuedBrowserMessages = new();
|
static BlockingCollection<byte[]> queuedBrowserMessages = new();
|
||||||
static CamelCasePropertyNamesContractResolver cr = new();
|
static CamelCasePropertyNamesContractResolver cr = new();
|
||||||
static StreamWriter log = new StreamWriter(new FileStream(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "native-host.log"), FileMode.Create));
|
//static StreamWriter log = new StreamWriter(new FileStream(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "native-host.log"), FileMode.Create));
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
//var json = BinaryToJson(new byte[0]);
|
try
|
||||||
|
{
|
||||||
|
var debugMode = Environment.GetEnvironmentVariable("XDM_DEBUG_MODE");
|
||||||
|
if (!string.IsNullOrEmpty(debugMode) && debugMode == "1")
|
||||||
|
{
|
||||||
|
var logFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "messaging-log.txt");
|
||||||
|
Trace.Listeners.Add(new TextWriterTraceListener(logFile, "myListener"));
|
||||||
|
Trace.AutoFlush = true;
|
||||||
|
}
|
||||||
|
Debug("Application_Startup");
|
||||||
|
if (args.Length > 0 && args[0].StartsWith("chrome-extension:"))
|
||||||
|
{
|
||||||
|
isFirefox = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
Debug("Process running from: " + AppDomain.CurrentDomain.BaseDirectory);
|
||||||
#if !NET35
|
#if !NET35
|
||||||
Debug(Environment.Is64BitProcess+"");
|
Debug("Is64BitProcess: " + Environment.Is64BitProcess);
|
||||||
#endif
|
#endif
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Debug("Trying to open mutex");
|
|
||||||
using var mutex = Mutex.OpenExisting(@"Global\XDM_Active_Instance");
|
|
||||||
Debug("Mutex opened");
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Debug("Mutex open failed, spawn xdm process...++");
|
|
||||||
CreateXDMInstance();
|
|
||||||
}
|
|
||||||
Debug("next");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
var inputReader = Console.OpenStandardInput();
|
var inputReader = Console.OpenStandardInput();
|
||||||
var outputWriter = Console.OpenStandardOutput();
|
var outputWriter = Console.OpenStandardOutput();
|
||||||
|
|
||||||
|
//var msg = ReadMessageBytes(inputReader);
|
||||||
|
//var message = Encoding.UTF8.GetString(msg);
|
||||||
|
//Debug(message);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Debug("Trying to open mutex");
|
||||||
|
using var mutex = Mutex.OpenExisting(@"Global\XDM_Active_Instance");
|
||||||
|
Debug("Mutex opened");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Debug("Mutex open failed, spawn xdm process...++");
|
||||||
|
CreateXDMInstance();
|
||||||
|
}
|
||||||
|
|
||||||
var t1 = new Thread(() =>
|
var t1 = new Thread(() =>
|
||||||
{
|
{
|
||||||
|
Debug("t1 reading messages from stdin sent by browser: ");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
//read from process stdin and write to blocking queue,
|
//read from process stdin and write to blocking queue,
|
||||||
//they will be sent to xdm once pipe handshake complets
|
//they will be sent to xdm once pipe handshake complets
|
||||||
var msg = ReadMessageBytes(inputReader);
|
Debug("Waiting for message - stdin...");
|
||||||
Debug(Encoding.UTF8.GetString(msg));
|
var msg = NativeMessageSerializer.ReadMessageBytes(inputReader, false);
|
||||||
|
Debug("Reading message from stdin - size: " + msg.Length);
|
||||||
|
Debug("Stdin - " + Encoding.UTF8.GetString(msg));
|
||||||
receivedBrowserMessages.Add(JsonToBinary(msg));
|
receivedBrowserMessages.Add(JsonToBinary(msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,10 +99,12 @@ namespace NativeHost
|
||||||
{
|
{
|
||||||
//read from blocking queue and write to stdout,
|
//read from blocking queue and write to stdout,
|
||||||
//these messages were queued by xdm
|
//these messages were queued by xdm
|
||||||
var msg = queuedBrowserMessages.Take();//doesn't make much sense to it async
|
var msg = queuedBrowserMessages.Take();
|
||||||
|
Debug("Sending to browser: " + Encoding.UTF8.GetString(msg));
|
||||||
var json = BinaryToJson(msg);
|
var json = BinaryToJson(msg);
|
||||||
|
Debug("Sending to browser: ");
|
||||||
Debug(Encoding.UTF8.GetString(json));
|
Debug(Encoding.UTF8.GetString(json));
|
||||||
WriteMessage(outputWriter, json);
|
NativeMessageSerializer.WriteMessage(outputWriter, json, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exx)
|
catch (Exception exx)
|
||||||
|
@ -101,21 +125,38 @@ namespace NativeHost
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CreateXDMInstance()
|
private static void CreateXDMInstance(bool minimized = true)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Debug("XDM instance creating...1");
|
var file = Path.Combine(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ".."),
|
||||||
ProcessStartInfo psi = new()
|
Environment.OSVersion.Platform == PlatformID.Win32NT ? "xdm-app.exe" : "xdm-app");
|
||||||
|
Debug("XDM instance creating...1 " + file);
|
||||||
|
if (isFirefox && Environment.OSVersion.Platform == PlatformID.Win32NT)
|
||||||
{
|
{
|
||||||
FileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "XDM.WinForm.UI.exe"),
|
var args = minimized ? " -m" : "";
|
||||||
UseShellExecute = true,
|
if (!NativeProcess.Win32CreateProcess(file, $"\"{file}\"{args}"))
|
||||||
Arguments = "-m"
|
{
|
||||||
};
|
Debug("Win32 create process failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ProcessStartInfo psi = new()
|
||||||
|
{
|
||||||
|
FileName = file,
|
||||||
|
UseShellExecute = true
|
||||||
|
};
|
||||||
|
|
||||||
Debug("XDM instance creating...");
|
if (minimized)
|
||||||
Process.Start(psi);
|
{
|
||||||
Debug("XDM instance created");
|
psi.Arguments = "-m";
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug("XDM instance creating...");
|
||||||
|
Process.Start(psi);
|
||||||
|
Debug("XDM instance created");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -125,56 +166,58 @@ namespace NativeHost
|
||||||
|
|
||||||
private static void ProcessMessages()
|
private static void ProcessMessages()
|
||||||
{
|
{
|
||||||
Debug("Log start");
|
Debug("start");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
NamedPipeServerStream inPipe = null;
|
//NamedPipeServerStream inPipe = null;
|
||||||
NamedPipeClientStream outPipe = null;
|
NamedPipeClientStream pipe = null;
|
||||||
while (true)
|
// while (true)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var pipeName = Guid.NewGuid().ToString();
|
//var pipeName = Guid.NewGuid().ToString();
|
||||||
inPipe = new NamedPipeServerStream(pipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.WriteThrough);
|
//inPipe = new NamedPipeServerStream(pipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.WriteThrough);
|
||||||
|
|
||||||
//start handshake with XDM
|
//start handshake with XDM
|
||||||
outPipe = new NamedPipeClientStream(".", "XDM_Ipc_Browser_Monitoring_Pipe", PipeDirection.Out);
|
pipe = new NamedPipeClientStream(".", "XDM_Ipc_Browser_Monitoring_Pipe", PipeDirection.InOut, PipeOptions.Asynchronous);
|
||||||
Debug("start handshake with XDM");
|
Debug("start handshake with XDM");
|
||||||
outPipe.Connect();
|
pipe.Connect();
|
||||||
WriteMessage(outPipe, pipeName);
|
//NativeMessageSerializer.WriteMessage(outPipe, pipeName);
|
||||||
Debug("pipename: " + pipeName);
|
//Debug("pipename: " + pipeName);
|
||||||
|
|
||||||
inPipe.WaitForConnection();
|
//inPipe.WaitForConnection();
|
||||||
var syncMsgBytes = ReadMessageBytes(inPipe);
|
//var syncMsgBytes = NativeMessageSerializer.ReadMessageBytes(inPipe);
|
||||||
Debug("No task message size: " + syncMsgBytes.Length);
|
//Debug("No task message size: " + syncMsgBytes.Length);
|
||||||
|
|
||||||
queuedBrowserMessages.Add(syncMsgBytes);
|
//queuedBrowserMessages.Add(syncMsgBytes);
|
||||||
|
|
||||||
//handshake with XDM is complete
|
//handshake with XDM is complete
|
||||||
Debug("handshake with XDM is complete");
|
Debug("handshake with XDM is complete");
|
||||||
|
|
||||||
using var waitHandle = new ManualResetEvent(false);
|
using var waitHandle = new ManualResetEvent(false);
|
||||||
|
|
||||||
//queue messages from xdm pipe for browser
|
//Direction: XDM ---> NativeHost
|
||||||
|
//Read messages from XDM's named pipe and add them to queuedBrowserMessages
|
||||||
var task1 = new Thread(() =>
|
var task1 = new Thread(() =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
var syncMsgBytes = ReadMessageBytes(inPipe);
|
var syncMsgBytes = NativeMessageSerializer.ReadMessageBytes(pipe);
|
||||||
//Debug("Task1 message size: " + syncMsgBytes.Length);
|
Debug("Message received from XDM of size: " + syncMsgBytes.Length);
|
||||||
if (syncMsgBytes.Length == 0)
|
if (syncMsgBytes.Length == 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Debug("Message from XDM: " + Encoding.UTF8.GetString(syncMsgBytes));
|
||||||
queuedBrowserMessages.Add(syncMsgBytes);
|
queuedBrowserMessages.Add(syncMsgBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug(ex.ToString());
|
Debug(ex.ToString(), ex);
|
||||||
queuedBrowserMessages.Add(Encoding.UTF8.GetBytes("{\"appExited\":\"true\"}"));
|
queuedBrowserMessages.Add(Encoding.UTF8.GetBytes("{\"appExited\":\"true\"}"));
|
||||||
}
|
}
|
||||||
waitHandle.Set();
|
waitHandle.Set();
|
||||||
|
@ -182,7 +225,8 @@ namespace NativeHost
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
//queue messages to xdm pipe from browser
|
//Direction: NativeHost ---> XDM
|
||||||
|
//Take messages from receivedBrowserMessages and write them to XDM's named pipe
|
||||||
var task2 = new Thread(() =>
|
var task2 = new Thread(() =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -190,7 +234,7 @@ namespace NativeHost
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
byte[] syncMsgBytes = null;
|
byte[] syncMsgBytes = null;
|
||||||
Debug("Task2 reading from browser stdin...");
|
Debug("Task2 reading messages queued by browser...");
|
||||||
syncMsgBytes = receivedBrowserMessages.Take();
|
syncMsgBytes = receivedBrowserMessages.Take();
|
||||||
if (syncMsgBytes.Length == 2 && (char)syncMsgBytes[0] == '{' && (char)syncMsgBytes[1] == '}')
|
if (syncMsgBytes.Length == 2 && (char)syncMsgBytes[0] == '{' && (char)syncMsgBytes[1] == '}')
|
||||||
{
|
{
|
||||||
|
@ -198,13 +242,14 @@ namespace NativeHost
|
||||||
throw new OperationCanceledException("Empty object");
|
throw new OperationCanceledException("Empty object");
|
||||||
}
|
}
|
||||||
//Debug("Task2 message size fron browser stdin: " + syncMsgBytes.Length);
|
//Debug("Task2 message size fron browser stdin: " + syncMsgBytes.Length);
|
||||||
//Debug(Encoding.UTF8.GetString(syncMsgBytes));
|
Debug("Sending message to XDM...");
|
||||||
WriteMessage(outPipe, syncMsgBytes);
|
NativeMessageSerializer.WriteMessage(pipe, syncMsgBytes);
|
||||||
|
Debug("Sent message to XDM");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug(ex.ToString());
|
Debug(ex.ToString(), ex);
|
||||||
}
|
}
|
||||||
waitHandle.Set();
|
waitHandle.Set();
|
||||||
Debug("Task2 finished");
|
Debug("Task2 finished");
|
||||||
|
@ -220,24 +265,29 @@ namespace NativeHost
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug(ex.ToString());
|
Debug(ex.ToString(), ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//try
|
||||||
|
//{
|
||||||
|
// inPipe.Disconnect();
|
||||||
|
//}
|
||||||
|
//catch { }
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
inPipe.Disconnect();
|
pipe.Close();
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
outPipe.Dispose();
|
pipe.Dispose();
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
try
|
|
||||||
{
|
|
||||||
inPipe.Dispose();
|
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
//try
|
||||||
|
//{
|
||||||
|
// inPipe.Dispose();
|
||||||
|
//}
|
||||||
|
//catch { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exxxx)
|
catch (Exception exxxx)
|
||||||
|
@ -246,67 +296,24 @@ namespace NativeHost
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Debug(string msg)
|
private static void Debug(string msg, Exception? ex2 = null)
|
||||||
{
|
{
|
||||||
try
|
Trace.WriteLine($"[xdm-native-messaging-host {DateTime.Now}] {msg}");
|
||||||
|
if (ex2 != null)
|
||||||
{
|
{
|
||||||
log.WriteLine(msg);
|
Trace.WriteLine($"[xdm-native-messaging-host {DateTime.Now}] {ex2}");
|
||||||
log.Flush();
|
|
||||||
//File.AppendAllText(@"c:\log.txt", msg + "\r\n");
|
|
||||||
Trace.WriteLine($"[{DateTime.Now}][NativeHost] {msg}");
|
|
||||||
}
|
|
||||||
catch(Exception ex)
|
|
||||||
{
|
|
||||||
log.WriteLine(ex.ToString());
|
|
||||||
log.Flush();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void WriteMessage(Stream pipe, string message)
|
//private static string ReadMessageString(Stream pipe)
|
||||||
{
|
//{
|
||||||
var msgBytes = Encoding.UTF8.GetBytes(message);
|
// var b4 = new byte[4];
|
||||||
WriteMessage(pipe, msgBytes);
|
// ReadFully(pipe, b4, 4);
|
||||||
}
|
// var syncLength = BitConverter.ToInt32(b4, 0);
|
||||||
|
// var bytes = new byte[syncLength];
|
||||||
private static void WriteMessage(Stream pipe, byte[] msgBytes)
|
// ReadFully(pipe, bytes, syncLength);
|
||||||
{
|
// return Encoding.UTF8.GetString(bytes);
|
||||||
pipe.Write(BitConverter.GetBytes(msgBytes.Length), 0, 4);
|
//}
|
||||||
pipe.Write(msgBytes, 0, msgBytes.Length);
|
|
||||||
pipe.Flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] ReadMessageBytes(Stream pipe)
|
|
||||||
{
|
|
||||||
var b4 = new byte[4];
|
|
||||||
ReadFully(pipe, b4, 4);
|
|
||||||
var syncLength = BitConverter.ToInt32(b4, 0);
|
|
||||||
var bytes = new byte[syncLength];
|
|
||||||
ReadFully(pipe, bytes, syncLength);
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ReadMessageString(Stream pipe)
|
|
||||||
{
|
|
||||||
var b4 = new byte[4];
|
|
||||||
ReadFully(pipe, b4, 4);
|
|
||||||
var syncLength = BitConverter.ToInt32(b4, 0);
|
|
||||||
var bytes = new byte[syncLength];
|
|
||||||
ReadFully(pipe, bytes, syncLength);
|
|
||||||
return Encoding.UTF8.GetString(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ReadFully(Stream stream, byte[] buf, int bytesToRead)
|
|
||||||
{
|
|
||||||
var rem = bytesToRead;
|
|
||||||
var index = 0;
|
|
||||||
while (rem > 0)
|
|
||||||
{
|
|
||||||
var c = stream.Read(buf, index, rem);
|
|
||||||
if (c == 0) throw new IOException("Unexpected EOF");
|
|
||||||
index += c;
|
|
||||||
rem -= c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] JsonToBinary(byte[] input)
|
private static byte[] JsonToBinary(byte[] input)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NativeHost
|
||||||
|
{
|
||||||
|
internal static class NativeProcess
|
||||||
|
{
|
||||||
|
internal static bool Win32CreateProcess(string? file, string? args)
|
||||||
|
{
|
||||||
|
const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;
|
||||||
|
const uint CREATE_BREAKAWAY_FROM_JOB = 0x01000000;
|
||||||
|
const uint CREATE_NEW_PROCESS_GROUP = 0x00000200;
|
||||||
|
PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION();
|
||||||
|
STARTUPINFO sInfo = new STARTUPINFO();
|
||||||
|
SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES();
|
||||||
|
SECURITY_ATTRIBUTES tSec = new SECURITY_ATTRIBUTES();
|
||||||
|
pSec.nLength = Marshal.SizeOf(pSec);
|
||||||
|
tSec.nLength = Marshal.SizeOf(tSec);
|
||||||
|
|
||||||
|
return CreateProcess(file, args,
|
||||||
|
ref pSec, ref tSec, false, CREATE_UNICODE_ENVIRONMENT | CREATE_BREAKAWAY_FROM_JOB | CREATE_NEW_PROCESS_GROUP,
|
||||||
|
IntPtr.Zero, null, ref sInfo, out pInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||||
|
internal static extern bool CreateProcess(
|
||||||
|
string? lpApplicationName,
|
||||||
|
string? lpCommandLine,
|
||||||
|
ref SECURITY_ATTRIBUTES lpProcessAttributes,
|
||||||
|
ref SECURITY_ATTRIBUTES lpThreadAttributes,
|
||||||
|
bool bInheritHandles,
|
||||||
|
uint dwCreationFlags,
|
||||||
|
IntPtr lpEnvironment,
|
||||||
|
string? lpCurrentDirectory,
|
||||||
|
[In] ref STARTUPINFO lpStartupInfo,
|
||||||
|
out PROCESS_INFORMATION lpProcessInformation);
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct SECURITY_ATTRIBUTES
|
||||||
|
{
|
||||||
|
public int nLength;
|
||||||
|
public IntPtr lpSecurityDescriptor;
|
||||||
|
public int bInheritHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This also works with CharSet.Ansi as long as the calling function uses the same character set.
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
|
internal struct STARTUPINFO
|
||||||
|
{
|
||||||
|
public Int32 cb;
|
||||||
|
public string lpReserved;
|
||||||
|
public string lpDesktop;
|
||||||
|
public string lpTitle;
|
||||||
|
public Int32 dwX;
|
||||||
|
public Int32 dwY;
|
||||||
|
public Int32 dwXSize;
|
||||||
|
public Int32 dwYSize;
|
||||||
|
public Int32 dwXCountChars;
|
||||||
|
public Int32 dwYCountChars;
|
||||||
|
public Int32 dwFillAttribute;
|
||||||
|
public Int32 dwFlags;
|
||||||
|
public Int16 wShowWindow;
|
||||||
|
public Int16 cbReserved2;
|
||||||
|
public IntPtr lpReserved2;
|
||||||
|
public IntPtr hStdInput;
|
||||||
|
public IntPtr hStdOutput;
|
||||||
|
public IntPtr hStdError;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct PROCESS_INFORMATION
|
||||||
|
{
|
||||||
|
public IntPtr hProcess;
|
||||||
|
public IntPtr hThread;
|
||||||
|
public int dwProcessId;
|
||||||
|
public int dwThreadId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,99 +0,0 @@
|
||||||
//using System;
|
|
||||||
//using System.Collections;
|
|
||||||
//using System.Collections.Generic;
|
|
||||||
//using System.Linq;
|
|
||||||
//using System.Text;
|
|
||||||
|
|
||||||
//namespace NetFX.Polyfill
|
|
||||||
//{
|
|
||||||
// public class ConcurrentDictionary<K, V> : IDictionary<K, V>
|
|
||||||
// {
|
|
||||||
// private Dictionary<K, V> dictionary;
|
|
||||||
|
|
||||||
// public V this[K key] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
|
||||||
|
|
||||||
// public ICollection<K> Keys => throw new NotImplementedException();
|
|
||||||
|
|
||||||
// public ICollection<V> Values => throw new NotImplementedException();
|
|
||||||
|
|
||||||
// public int Count => throw new NotImplementedException();
|
|
||||||
|
|
||||||
// public bool IsReadOnly => throw new NotImplementedException();
|
|
||||||
|
|
||||||
// public void Add(K key, V value)
|
|
||||||
// {
|
|
||||||
// throw new NotImplementedException();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public void Add(KeyValuePair<K, V> item)
|
|
||||||
// {
|
|
||||||
// throw new NotImplementedException();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public void Clear()
|
|
||||||
// {
|
|
||||||
// throw new NotImplementedException();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public bool Contains(KeyValuePair<K, V> item)
|
|
||||||
// {
|
|
||||||
// throw new NotImplementedException();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public bool ContainsKey(K key)
|
|
||||||
// {
|
|
||||||
// throw new NotImplementedException();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
|
|
||||||
// {
|
|
||||||
// throw new NotImplementedException();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
|
|
||||||
// {
|
|
||||||
// lock (this)
|
|
||||||
// {
|
|
||||||
// foreach (var item in dictionary)
|
|
||||||
// {
|
|
||||||
// yield return item;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public bool Remove(K key)
|
|
||||||
// {
|
|
||||||
// lock (this)
|
|
||||||
// {
|
|
||||||
// return dictionary.Remove(key);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public bool Remove(KeyValuePair<K, V> item)
|
|
||||||
// {
|
|
||||||
// lock (this)
|
|
||||||
// {
|
|
||||||
// return dictionary.Remove(item.Key);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public bool TryGetValue(K key, out V value)
|
|
||||||
// {
|
|
||||||
// lock (this)
|
|
||||||
// {
|
|
||||||
// return dictionary.TryGetValue(key, out value);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// IEnumerator IEnumerable.GetEnumerator()
|
|
||||||
// {
|
|
||||||
// lock (this)
|
|
||||||
// {
|
|
||||||
// foreach (var item in dictionary)
|
|
||||||
// {
|
|
||||||
// yield return item;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
|
@ -1,18 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFrameworks>net3.5;net4.5;net4.7.2</TargetFrameworks>
|
|
||||||
<LangVersion>9.0</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<Platforms>AnyCPU;x86</Platforms>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="DotNetZip" Version="1.12.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' != 'net3.5' ">
|
|
||||||
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
|
@ -1,127 +0,0 @@
|
||||||
//using System;
|
|
||||||
//using System.Reflection;
|
|
||||||
//using System.Text;
|
|
||||||
//using XDM.Core.Lib.Common;
|
|
||||||
|
|
||||||
//namespace SerializeGenerator
|
|
||||||
//{
|
|
||||||
// class Program
|
|
||||||
// {
|
|
||||||
// static int c = 1;
|
|
||||||
// static void Main(string[] args)
|
|
||||||
// {
|
|
||||||
// var type = typeof(Config);
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private static void GenerateCode(Type type, StringBuilder serializer, StringBuilder deserializer)
|
|
||||||
// {
|
|
||||||
// serializer.Append(@"
|
|
||||||
// private static void WriteString(BinaryWriter w, string value, string name)
|
|
||||||
// {
|
|
||||||
// w.Write(name);
|
|
||||||
// w.Write(STRING);
|
|
||||||
// w.Write(value ?? string.Empty);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private static void WriteBoolean(BinaryWriter w, bool value, string name)
|
|
||||||
// {
|
|
||||||
// w.Write(name);
|
|
||||||
// w.Write(BOOL);
|
|
||||||
// w.Write(value);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private static void WriteInt32(BinaryWriter w, int value, string name)
|
|
||||||
// {
|
|
||||||
// w.Write(name);
|
|
||||||
// w.Write(INT);
|
|
||||||
// w.Write(value);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private static void WriteStringArray(BinaryWriter w, IEnumerable<string> array, string name, int count)
|
|
||||||
// {
|
|
||||||
// w.Write(name);
|
|
||||||
// w.Write(STRING_ARRAY);
|
|
||||||
// w.Write((short)count);
|
|
||||||
// foreach (var item in array)
|
|
||||||
// {
|
|
||||||
// w.Write(item);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private
|
|
||||||
//");
|
|
||||||
|
|
||||||
// foreach (var property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
|
|
||||||
// {
|
|
||||||
// if (property.PropertyType.IsArray || property.PropertyType.GetInterface("IEnumerable") != null)
|
|
||||||
// {
|
|
||||||
// if (property.PropertyType.IsArray)
|
|
||||||
// {
|
|
||||||
// serializer.Append($"var c{c++}=obj.{property.Name}?.Length??0;\r\n");
|
|
||||||
// serializer.Append($"writer.Write(c{c});\r\n");
|
|
||||||
// serializer.Append($"if(obj.{property.Name}!=null)" + "{foreach(var item in obj." + property.Name + "){");
|
|
||||||
// //deserializer.Append($"var c{c++}=reader.ReadInt32();\r\n");
|
|
||||||
// //deserializer.Append("if(c" + c + ">0){");
|
|
||||||
|
|
||||||
// GenerateCode(property.PropertyType, serializer, deserializer);
|
|
||||||
// serializer.Append("}}\r\n");
|
|
||||||
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (property.PropertyType == typeof(string)
|
|
||||||
// || property.PropertyType == typeof(int)
|
|
||||||
// || property.PropertyType == typeof(long)
|
|
||||||
// || property.PropertyType == typeof(bool)
|
|
||||||
// || property.PropertyType == typeof(double)
|
|
||||||
// || property.PropertyType == typeof(float)
|
|
||||||
// || property.PropertyType.IsEnum)
|
|
||||||
// {
|
|
||||||
// if (property.PropertyType == typeof(string))
|
|
||||||
// {
|
|
||||||
// serializer.Append($"writer.Write(obj.{property.Name}??string.Empty);\r\n");
|
|
||||||
// deserializer.Append($"obj.{property.Name}=Helper.ReadString(reader);\r\n");
|
|
||||||
// }
|
|
||||||
// else if (property.PropertyType.IsEnum)
|
|
||||||
// {
|
|
||||||
// serializer.Append($"writer.Write((int)obj.{property.Name});\r\n");
|
|
||||||
// deserializer.Append($"obj.{property.Name}=({property.PropertyType})reader.ReadInt32();\r\n");
|
|
||||||
// }
|
|
||||||
// else if (property.PropertyType == typeof(int))
|
|
||||||
// {
|
|
||||||
// serializer.Append($"writer.Write(obj.{property.Name});\r\n");
|
|
||||||
// deserializer.Append($"obj.{property.Name}=reader.ReadInt32();\r\n");
|
|
||||||
// }
|
|
||||||
// else if (property.PropertyType == typeof(long))
|
|
||||||
// {
|
|
||||||
// serializer.Append($"writer.Write(obj.{property.Name});\r\n");
|
|
||||||
// deserializer.Append($"obj.{property.Name}=reader.ReadInt64();\r\n");
|
|
||||||
// }
|
|
||||||
// else if (property.PropertyType == typeof(bool))
|
|
||||||
// {
|
|
||||||
// serializer.Append($"writer.Write(obj.{property.Name});\r\n");
|
|
||||||
// deserializer.Append($"obj.{property.Name}=reader.ReadBoolean();\r\n");
|
|
||||||
// }
|
|
||||||
// else if (property.PropertyType == typeof(double))
|
|
||||||
// {
|
|
||||||
// serializer.Append($"writer.Write(obj.{property.Name});\r\n");
|
|
||||||
// deserializer.Append($"obj.{property.Name}=reader.ReadDouble();\r\n");
|
|
||||||
// }
|
|
||||||
// else if (property.PropertyType == typeof(float))
|
|
||||||
// {
|
|
||||||
// serializer.Append($"writer.Write(obj.{property.Name});\r\n");
|
|
||||||
// deserializer.Append($"obj.{property.Name}=reader.ReadFloat();\r\n");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// if (property.PropertyType.IsValueType || property.PropertyType.IsClass)
|
|
||||||
// {
|
|
||||||
// GenerateCode(property.PropertyType, serializer, deserializer);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
|
@ -1,12 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\XDM_CoreFx\XDM.Core.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
|
@ -1,18 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace TraceLog
|
|
||||||
{
|
|
||||||
public static class Log
|
|
||||||
{
|
|
||||||
public static void Debug(object obj, string message)
|
|
||||||
{
|
|
||||||
Trace.WriteLine(message + " : " + obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Debug(string message)
|
|
||||||
{
|
|
||||||
Trace.WriteLine(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFrameworks>net3.5;net4.5;net4.7.2;net5.0</TargetFrameworks>
|
|
||||||
<LangVersion>9.0</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<Platforms>AnyCPU;x86</Platforms>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
|
@ -10,12 +10,12 @@ namespace Translations
|
||||||
|
|
||||||
static TextResource()
|
static TextResource()
|
||||||
{
|
{
|
||||||
Load("English");
|
Load("English.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Load(string language)
|
public static void Load(string language)
|
||||||
{
|
{
|
||||||
var file = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Path.Combine("Lang", $"{language}.txt"));
|
var file = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Path.Combine("Lang", $"{language}"));
|
||||||
if (File.Exists(file))
|
if (File.Exists(file))
|
||||||
{
|
{
|
||||||
LoadTexts(file);
|
LoadTexts(file);
|
||||||
|
@ -46,5 +46,10 @@ namespace Translations
|
||||||
texts[key] = val;
|
texts[key] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<string> GetKeys()
|
||||||
|
{
|
||||||
|
return texts.Keys;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,14 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net3.5;net4.5;net4.7.2;net5.0</TargetFrameworks>
|
<TargetFrameworks>net3.5;net4.5;net4.7.2;net5.0</TargetFrameworks>
|
||||||
<Platforms>AnyCPU;x86</Platforms>
|
<Platforms>AnyCPU;x86;x64</Platforms>
|
||||||
<LangVersion>9.0</LangVersion>
|
<LangVersion>9.0</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net4.7.2|x86'">
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
<DebugSymbols>false</DebugSymbols>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -32,10 +32,17 @@ namespace BrowserMonitoring
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var rawMessage = envelop.Message;
|
if (envelop.MessageType == "custom")
|
||||||
if (rawMessage == null)
|
|
||||||
{
|
{
|
||||||
Log.Debug("Raw message is null");
|
var args = ArgsProcessor.ParseArgs(envelop.CustomData.Split('\r'));
|
||||||
|
ArgsProcessor.Process(app, args);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rawMessage = envelop.Message;
|
||||||
|
if (rawMessage == null && envelop.Messages == null)
|
||||||
|
{
|
||||||
|
Log.Debug("Raw message/messages is null");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -50,6 +57,17 @@ namespace BrowserMonitoring
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "links":
|
||||||
|
{
|
||||||
|
var messages = new List<Message>(envelop.Messages.Length);
|
||||||
|
foreach (var msg in envelop.Messages)
|
||||||
|
{
|
||||||
|
var message = Parse(msg);
|
||||||
|
messages.Add(message);
|
||||||
|
}
|
||||||
|
app.AddBatchLinks(messages);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "video":
|
case "video":
|
||||||
{
|
{
|
||||||
var message = Parse(rawMessage);
|
var message = Parse(rawMessage);
|
|
@ -40,7 +40,6 @@ namespace BrowserMonitoring
|
||||||
Log.Debug(ex.ToString());
|
Log.Debug(ex.ToString());
|
||||||
app.AppUI.ShowMessageBox(null, TextResource.GetText("MSG_ALREADY_RUNNING"));
|
app.AppUI.ShowMessageBox(null, TextResource.GetText("MSG_ALREADY_RUNNING"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}).Start();
|
}).Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,46 +62,59 @@ namespace BrowserMonitoring
|
||||||
switch (context.RequestPath)
|
switch (context.RequestPath)
|
||||||
{
|
{
|
||||||
case "/download":
|
case "/download":
|
||||||
Log.Debug(Encoding.UTF8.GetString(context.RequestBody!));
|
|
||||||
var message = Message.ParseMessage(Encoding.UTF8.GetString(context.RequestBody!));
|
|
||||||
if (!(Helpers.IsBlockedHost(message.Url) || Helpers.IsCompressedJSorCSS(message.Url)))
|
|
||||||
{
|
{
|
||||||
app.AddDownload(message);
|
var text = Encoding.UTF8.GetString(context.RequestBody!);
|
||||||
}
|
Log.Debug(text);
|
||||||
break;
|
var message = Message.ParseMessage(text);
|
||||||
case "/video":
|
if (!(Helpers.IsBlockedHost(message.Url) || Helpers.IsCompressedJSorCSS(message.Url)))
|
||||||
Console.WriteLine(Encoding.UTF8.GetString(context.RequestBody!));
|
|
||||||
var message2 = Message.ParseMessage(Encoding.UTF8.GetString(context.RequestBody!));
|
|
||||||
var contentType = message2.GetResponseHeaderFirstValue("Content-Type")?.ToLowerInvariant() ?? string.Empty;
|
|
||||||
|
|
||||||
if (VideoUrlHelper.IsHLS(contentType))
|
|
||||||
{
|
|
||||||
VideoUrlHelper.ProcessHLSVideo(message2, app);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (VideoUrlHelper.IsDASH(contentType))
|
|
||||||
{
|
|
||||||
VideoUrlHelper.ProcessDashVideo(message2, app);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!VideoUrlHelper.ProcessYtDashSegment(message2, app))
|
|
||||||
{
|
|
||||||
if (contentType != null && !(contentType.Contains("f4f") ||
|
|
||||||
contentType.Contains("m4s") ||
|
|
||||||
contentType.Contains("mp2t") || message2.Url.Contains("abst") ||
|
|
||||||
message2.Url.Contains("f4x") || message2.Url.Contains(".fbcdn")
|
|
||||||
|| message2.Url.Contains("http://127.0.0.1:9614")))
|
|
||||||
{
|
{
|
||||||
VideoUrlHelper.ProcessNormalVideo(message2, app);
|
app.AddDownload(message);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
case "/video":
|
||||||
case "/item":
|
|
||||||
foreach (var item in Encoding.UTF8.GetString(context.RequestBody!).Split(new char[] { '\r', '\n' }))
|
|
||||||
{
|
{
|
||||||
app.AddVideoDownload(item);
|
var text = Encoding.UTF8.GetString(context.RequestBody!);
|
||||||
|
Log.Debug(text);
|
||||||
|
var message2 = Message.ParseMessage(Encoding.UTF8.GetString(context.RequestBody!));
|
||||||
|
var contentType = message2.GetResponseHeaderFirstValue("Content-Type")?.ToLowerInvariant() ?? string.Empty;
|
||||||
|
if (VideoUrlHelper.IsHLS(contentType))
|
||||||
|
{
|
||||||
|
VideoUrlHelper.ProcessHLSVideo(message2, app);
|
||||||
|
}
|
||||||
|
if (VideoUrlHelper.IsDASH(contentType))
|
||||||
|
{
|
||||||
|
VideoUrlHelper.ProcessDashVideo(message2, app);
|
||||||
|
}
|
||||||
|
if (!VideoUrlHelper.ProcessYtDashSegment(message2, app))
|
||||||
|
{
|
||||||
|
if (contentType != null && !(contentType.Contains("f4f") ||
|
||||||
|
contentType.Contains("m4s") ||
|
||||||
|
contentType.Contains("mp2t") || message2.Url.Contains("abst") ||
|
||||||
|
message2.Url.Contains("f4x") || message2.Url.Contains(".fbcdn")
|
||||||
|
|| message2.Url.Contains("http://127.0.0.1:9614")))
|
||||||
|
{
|
||||||
|
VideoUrlHelper.ProcessNormalVideo(message2, app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "/links":
|
||||||
|
{
|
||||||
|
var text = Encoding.UTF8.GetString(context.RequestBody!);
|
||||||
|
Log.Debug(text);
|
||||||
|
var arr = text.Split(new string[] { "\r\n\r\n" }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
app.AddBatchLinks(arr.Select(str => Message.ParseMessage(str.Trim())).ToList());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "/item":
|
||||||
|
{
|
||||||
|
foreach (var item in Encoding.UTF8.GetString(context.RequestBody!).Split(new char[] { '\r', '\n' }))
|
||||||
|
{
|
||||||
|
app.AddVideoDownload(item);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case "/clear":
|
case "/clear":
|
||||||
app.ClearVideoList();
|
app.ClearVideoList();
|
||||||
break;
|
break;
|
|
@ -0,0 +1,217 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.IO;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BrowserMonitoring
|
||||||
|
{
|
||||||
|
public class JsonMessageParser
|
||||||
|
{
|
||||||
|
private T? ReadProperty<T>(JsonTextReader reader, string name)
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.PropertyName && reader.Value?.ToString() == name &&
|
||||||
|
reader.Read() && reader.Value != null)
|
||||||
|
{
|
||||||
|
return (T)reader.Value;
|
||||||
|
}
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsObjectStart(JsonTextReader reader, string name)
|
||||||
|
{
|
||||||
|
return reader.TokenType == JsonToken.PropertyName && reader.Value?.ToString() == name &&
|
||||||
|
reader.Read() && reader.TokenType == JsonToken.StartObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsListStart(JsonTextReader reader, string name)
|
||||||
|
{
|
||||||
|
return reader.TokenType == JsonToken.PropertyName && reader.Value?.ToString() == name &&
|
||||||
|
reader.Read() && reader.TokenType == JsonToken.StartArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SkipUnknownParts(JsonTextReader reader)
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.PropertyName && reader.Value != null)
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.StartObject)
|
||||||
|
{
|
||||||
|
var n = 1;
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.EndObject) n--;
|
||||||
|
if (reader.TokenType == JsonToken.StartObject) n++;
|
||||||
|
if (n == 0) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (reader.TokenType == JsonToken.StartArray)
|
||||||
|
{
|
||||||
|
var n = 1;
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.EndArray) n--;
|
||||||
|
if (reader.TokenType == JsonToken.StartArray) n++;
|
||||||
|
if (n == 0) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (reader.Value != null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private RawBrowserMessage ReadMessageObject(JsonTextReader reader)
|
||||||
|
{
|
||||||
|
var msg = new RawBrowserMessage { Cookies = new(), ResponseHeaders = new(), RequestHeaders = new() };
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.EndObject) break;
|
||||||
|
var url = ReadProperty<string>(reader, "url");
|
||||||
|
if (url != null)
|
||||||
|
{
|
||||||
|
msg.Url = url;
|
||||||
|
}
|
||||||
|
if (IsObjectStart(reader, "cookies"))
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.EndObject) break;
|
||||||
|
if (reader.TokenType == JsonToken.PropertyName && reader.Value != null)
|
||||||
|
{
|
||||||
|
var cookieName = (string)reader.Value;
|
||||||
|
if (reader.Read() && reader.TokenType == JsonToken.String)
|
||||||
|
{
|
||||||
|
var cookieValue = (string)reader.Value;
|
||||||
|
msg.Cookies[cookieName] = cookieValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsObjectStart(reader, "responseHeaders"))// && IsListStart(reader, "realUA"))
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.EndObject) break;
|
||||||
|
if (reader.TokenType == JsonToken.PropertyName && reader.Value != null)
|
||||||
|
{
|
||||||
|
var headerName = (string)reader.Value;
|
||||||
|
if (IsListStart(reader, headerName))
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.EndArray) break;
|
||||||
|
if (reader.TokenType == JsonToken.String)
|
||||||
|
{
|
||||||
|
if (msg.ResponseHeaders.TryGetValue(headerName, out var list))
|
||||||
|
{
|
||||||
|
list.Add((string)reader.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
msg.ResponseHeaders[headerName] = new() { (string)reader.Value };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsObjectStart(reader, "requestHeaders"))
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.EndObject) break;
|
||||||
|
if (reader.TokenType == JsonToken.PropertyName && reader.Value != null)
|
||||||
|
{
|
||||||
|
var headerName = (string)reader.Value;
|
||||||
|
if (IsListStart(reader, headerName))
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.EndArray) break;
|
||||||
|
if (reader.TokenType == JsonToken.String)
|
||||||
|
{
|
||||||
|
if (msg.RequestHeaders.TryGetValue(headerName, out var list))
|
||||||
|
{
|
||||||
|
list.Add((string)reader.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
msg.RequestHeaders[headerName] = new() { (string)reader.Value };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SkipUnknownParts(reader);
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RawBrowserMessageEnvelop Parse(Stream stream)
|
||||||
|
{
|
||||||
|
var envelop = new RawBrowserMessageEnvelop();
|
||||||
|
var reader = new JsonTextReader(new StreamReader(stream));
|
||||||
|
if (reader.Read() && reader.TokenType == JsonToken.StartObject)
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.EndObject) break;
|
||||||
|
var messageType = ReadProperty<string>(reader, "messageType");
|
||||||
|
if (messageType != null)
|
||||||
|
{
|
||||||
|
envelop.MessageType = messageType;
|
||||||
|
}
|
||||||
|
var customData = ReadProperty<string>(reader, "customData");
|
||||||
|
if (customData != null)
|
||||||
|
{
|
||||||
|
envelop.CustomData = customData;
|
||||||
|
}
|
||||||
|
if (IsObjectStart(reader, "message"))
|
||||||
|
{
|
||||||
|
var msg = ReadMessageObject(reader);
|
||||||
|
envelop.Message = msg;
|
||||||
|
}
|
||||||
|
if (IsListStart(reader, "messages"))
|
||||||
|
{
|
||||||
|
var list = new List<RawBrowserMessage>();
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.EndArray) break;
|
||||||
|
if (reader.TokenType == JsonToken.StartObject)
|
||||||
|
{
|
||||||
|
var msg = ReadMessageObject(reader);
|
||||||
|
list.Add(msg);
|
||||||
|
envelop.Messages = list.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (IsListStart(reader, "videoIds"))
|
||||||
|
{
|
||||||
|
var list = new List<RawBrowserMessage>();
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.EndArray) break;
|
||||||
|
if (reader.TokenType == JsonToken.StartObject)
|
||||||
|
{
|
||||||
|
var msg = ReadMessageObject(reader);
|
||||||
|
list.Add(msg);
|
||||||
|
envelop.Messages = list.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SkipUnknownParts(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return envelop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using TraceLog;
|
||||||
|
using XDM.Core.Lib.Common;
|
||||||
|
|
||||||
|
namespace BrowserMonitoring
|
||||||
|
{
|
||||||
|
internal static class NativeMessagingConfigurer
|
||||||
|
{
|
||||||
|
private static void CreateMessagingHostManifest(Browser browser, string appName, string manifestPath)
|
||||||
|
{
|
||||||
|
var allowedExtensions = browser == Browser.Firefox ? new[] {
|
||||||
|
"browser-mon@xdman.sourceforge.net"
|
||||||
|
} : new[] {
|
||||||
|
"chrome-extension://danmljfachfhpbfikjgedlfifabhofcj/",
|
||||||
|
"chrome-extension://dkckaoghoiffdbomfbbodbbgmhjblecj/",
|
||||||
|
"chrome-extension://ejpbcmllmliidhlpkcgbphhmaodjihnc/",
|
||||||
|
"chrome-extension://fogpiboapmefmkbodpmfnohfflonbgig/"
|
||||||
|
};
|
||||||
|
var folder = Path.GetDirectoryName(manifestPath)!;
|
||||||
|
if (!Directory.Exists(folder))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(folder);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Debug(ex, ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
using var stream = new FileStream(manifestPath, FileMode.Create);
|
||||||
|
using var textWriter = new StreamWriter(stream);
|
||||||
|
using var writer = new JsonTextWriter(textWriter);
|
||||||
|
writer.Formatting = Formatting.Indented;
|
||||||
|
writer.WriteStartObject();
|
||||||
|
writer.WritePropertyName("name");
|
||||||
|
writer.WriteValue(appName);
|
||||||
|
writer.WritePropertyName("description");
|
||||||
|
writer.WriteValue("Native messaging host for Xtreme Download Manager");
|
||||||
|
writer.WritePropertyName("path");
|
||||||
|
writer.WriteValue(Path.Combine(
|
||||||
|
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "MessagingHost"),
|
||||||
|
Environment.OSVersion.Platform == PlatformID.Win32NT ?
|
||||||
|
"xdm-messaging-host.exe" : "xdm-messaging-host"));
|
||||||
|
writer.WritePropertyName("type");
|
||||||
|
writer.WriteValue("stdio");
|
||||||
|
writer.WritePropertyName(browser == Browser.Firefox ? "allowed_extensions" : "allowed_origins");
|
||||||
|
writer.WriteStartArray();
|
||||||
|
foreach (var extension in allowedExtensions)
|
||||||
|
{
|
||||||
|
writer.WriteValue(extension);
|
||||||
|
}
|
||||||
|
writer.WriteEndArray();
|
||||||
|
writer.WriteEndObject();
|
||||||
|
writer.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InstallNativeMessagingHost(Browser browser)
|
||||||
|
{
|
||||||
|
var appName = browser == Browser.Firefox ? "xdmff.native_host" :
|
||||||
|
"xdm_chrome.native_host";
|
||||||
|
var os = Environment.OSVersion.Platform;
|
||||||
|
if (os == PlatformID.Win32NT)
|
||||||
|
{
|
||||||
|
var manifestPath = Path.Combine(Config.DataDir, $"{appName}.json");
|
||||||
|
CreateMessagingHostManifest(browser, appName, manifestPath);
|
||||||
|
var regPath = (browser == Browser.Firefox ?
|
||||||
|
@"Software\Mozilla\NativeMessagingHosts\" :
|
||||||
|
@"SOFTWARE\Google\Chrome\NativeMessagingHosts");
|
||||||
|
using var regKey = Registry.CurrentUser.CreateSubKey(regPath);
|
||||||
|
using var key = regKey.CreateSubKey(appName, RegistryKeyPermissionCheck.ReadWriteSubTree);
|
||||||
|
key.SetValue(null, manifestPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if NET5_0_OR_GREATER
|
||||||
|
string manifestPath;
|
||||||
|
var home = Environment.GetEnvironmentVariable("HOME")!;
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
{
|
||||||
|
manifestPath = Path.Combine(home, browser == Browser.Firefox ?
|
||||||
|
$"Library/Application Support/Mozilla/NativeMessagingHosts/{appName}.json" :
|
||||||
|
$"Library/Application Support/Google/Chrome/NativeMessagingHosts/{appName}.json");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
manifestPath = Path.Combine(home, browser == Browser.Firefox ?
|
||||||
|
$".mozilla/native-messaging-hosts/{appName}.json" :
|
||||||
|
$".config/google-chrome/NativeMessagingHosts/{appName}.json");
|
||||||
|
}
|
||||||
|
Log.Debug($"Manifest file: {manifestPath}");
|
||||||
|
CreateMessagingHostManifest(browser, appName, manifestPath);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Browser
|
||||||
|
{
|
||||||
|
Chrome, Firefox, MSEdge
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
using System;
|
||||||
|
using System.IO.Pipes;
|
||||||
|
using System.Threading;
|
||||||
|
using TraceLog;
|
||||||
|
|
||||||
|
namespace BrowserMonitoring
|
||||||
|
{
|
||||||
|
internal sealed class NativeMessagingHostChannel
|
||||||
|
{
|
||||||
|
private NamedPipeServerStream pipe;
|
||||||
|
internal event EventHandler<NativeMessageEventArgs>? MessageReceived;
|
||||||
|
internal event EventHandler? Disconnected;
|
||||||
|
private Thread readerThread;
|
||||||
|
|
||||||
|
internal NativeMessagingHostChannel(NamedPipeServerStream pipe)
|
||||||
|
{
|
||||||
|
this.pipe = pipe;
|
||||||
|
readerThread = new Thread(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ReadDataFromPipe();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Debug(ex, ex.ToString());
|
||||||
|
Disconnect();
|
||||||
|
}
|
||||||
|
this.Disconnected?.Invoke(this, EventArgs.Empty);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Disconnect()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (pipe.IsConnected)
|
||||||
|
{
|
||||||
|
pipe.Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Debug(ex, ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Publish(byte[] data)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
NativeMessageSerializer.WriteMessage(pipe, data);
|
||||||
|
pipe.Flush();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Debug(ex, ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ReadDataFromPipe()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Log.Debug("Waiting for message from native host...");
|
||||||
|
var bytes = NativeMessageSerializer.ReadMessageBytes(pipe);
|
||||||
|
Log.Debug($"Message from native host {bytes.Length} bytes");
|
||||||
|
this.MessageReceived?.Invoke(this, new NativeMessageEventArgs(bytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Start(byte[] initialConfig)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
readerThread.Start();
|
||||||
|
NativeMessageSerializer.WriteMessage(pipe, initialConfig);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Debug(ex, ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class NativeMessageEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
private byte[] data;
|
||||||
|
|
||||||
|
internal NativeMessageEventArgs(byte[] data)
|
||||||
|
{
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal byte[] Data => data;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,367 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.IO.Pipes;
|
||||||
|
using System.IO;
|
||||||
|
using XDM.Core.Lib.Common;
|
||||||
|
using System.Threading;
|
||||||
|
#if NET35
|
||||||
|
using NetFX.Polyfill;
|
||||||
|
#else
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
#endif
|
||||||
|
using TraceLog;
|
||||||
|
|
||||||
|
namespace BrowserMonitoring
|
||||||
|
{
|
||||||
|
public class NativeMessagingHostHandler : IDisposable
|
||||||
|
{
|
||||||
|
private int MaxPipeInstance = 254;
|
||||||
|
private static readonly string PipeName = "XDM_Ipc_Browser_Monitoring_Pipe";
|
||||||
|
private List<NativeMessagingHostChannel> connectedChannels = new();
|
||||||
|
//private List<NamedPipeServerStream> inPipes = new();
|
||||||
|
//private Dictionary<NamedPipeServerStream, NamedPipeClientStream> inOutMap = new();
|
||||||
|
private readonly IApp app;
|
||||||
|
private static Mutex globalMutex;
|
||||||
|
// private readonly BlockingCollection<byte[]> Messages = new();
|
||||||
|
//private Thread WriterThread;
|
||||||
|
private Thread listenerThread;
|
||||||
|
|
||||||
|
public static void EnsureSingleInstance(IApp app)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var mutex = Mutex.OpenExisting(@"Global\XDM_Active_Instance");
|
||||||
|
throw new InstanceAlreadyRunningException(@"XDM instance already running, Mutex exists 'Global\XDM_Active_Instance'");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Debug(ex, "Exception in NativeMessagingHostHandler ctor");
|
||||||
|
if (ex is InstanceAlreadyRunningException)
|
||||||
|
{
|
||||||
|
if (app.Args != null && app.Args.Length > 0)
|
||||||
|
{
|
||||||
|
Log.Debug(ex, "Sending args to running instance");
|
||||||
|
SendArgsToRunningInstance(app.Args);
|
||||||
|
Environment.Exit(0);
|
||||||
|
}
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
globalMutex = new Mutex(true, @"Global\XDM_Active_Instance");
|
||||||
|
}
|
||||||
|
|
||||||
|
public NativeMessagingHostHandler(IApp app)
|
||||||
|
{
|
||||||
|
this.app = app;
|
||||||
|
EnsureSingleInstance(this.app);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BroadcastConfig()
|
||||||
|
{
|
||||||
|
var bytes = GetSyncBytes(app);
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
foreach (var channel in connectedChannels)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
channel.Publish(bytes);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Debug(ex, ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Messages.Add(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartPipedChannel()
|
||||||
|
{
|
||||||
|
listenerThread = new Thread(() =>
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var pipe =
|
||||||
|
new NamedPipeServerStream(PipeName,
|
||||||
|
PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances,
|
||||||
|
PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
|
||||||
|
Log.Debug("Waiting for native host pipe...");
|
||||||
|
pipe.WaitForConnection();
|
||||||
|
Log.Debug("Pipe request received");
|
||||||
|
lock (connectedChannels)
|
||||||
|
{
|
||||||
|
var channel = CreateChannel(pipe);
|
||||||
|
connectedChannels.Add(channel);
|
||||||
|
channel.Start(GetSyncBytes(app));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
listenerThread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private NativeMessagingHostChannel CreateChannel(NamedPipeServerStream pipe)
|
||||||
|
{
|
||||||
|
var channel = new NativeMessagingHostChannel(pipe);
|
||||||
|
channel.MessageReceived += (sender, args) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var br = new BinaryReader(new MemoryStream(args.Data));
|
||||||
|
var envelop = RawBrowserMessageEnvelop.Deserialize(br);
|
||||||
|
BrowserMessageHandler.Handle(app, envelop);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Debug(ex, ex.ToString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
channel.Disconnected += (sender, bytes) =>
|
||||||
|
{
|
||||||
|
lock (connectedChannels)
|
||||||
|
{
|
||||||
|
connectedChannels.Remove((NativeMessagingHostChannel)sender);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
//public void StartPipedChannel()
|
||||||
|
//{
|
||||||
|
// WriterThread = new Thread(() =>
|
||||||
|
// {
|
||||||
|
// while (true)
|
||||||
|
// {
|
||||||
|
// //Log.Debug("Total messages to be sent to native host: " + Messages.Count);
|
||||||
|
// var bytes = Messages.Take();
|
||||||
|
// foreach (var key in inOutMap.Keys)
|
||||||
|
// {
|
||||||
|
// //Log.Debug("Sending message to native host");
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// var outpipe = inOutMap[key];
|
||||||
|
// NativeMessageSerializer.WriteMessage(outpipe, bytes);
|
||||||
|
// //Log.Debug("Send message to native host successfully");
|
||||||
|
// }
|
||||||
|
// catch (Exception ex)
|
||||||
|
// {
|
||||||
|
// Log.Debug(ex, "Send message to native host failed");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// WriterThread.Start();
|
||||||
|
// new Thread(() =>
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// if (inPipes.Count == MaxPipeInstance)
|
||||||
|
// {
|
||||||
|
// Log.Debug("Max pipe count of " + MaxPipeInstance + " is reached");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// var inPipe =
|
||||||
|
// new NamedPipeServerStream(PipeName,
|
||||||
|
// PipeDirection.In, NamedPipeServerStream.MaxAllowedServerInstances,
|
||||||
|
// PipeTransmissionMode.Byte, PipeOptions.WriteThrough);
|
||||||
|
// inPipes.Add(inPipe);
|
||||||
|
// var first = true;
|
||||||
|
// while (true)
|
||||||
|
// {
|
||||||
|
// Log.Debug("Waiting for native host pipe...");
|
||||||
|
// inPipe.WaitForConnection();
|
||||||
|
// Log.Debug("Pipe request received");
|
||||||
|
|
||||||
|
// if (first)
|
||||||
|
// {
|
||||||
|
// Log.Debug("Creating one more additional pipe");
|
||||||
|
// StartPipedChannel();
|
||||||
|
// first = false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// ConsumePipe(inPipe);
|
||||||
|
// }
|
||||||
|
// catch (Exception e)
|
||||||
|
// {
|
||||||
|
// inPipe.Disconnect();
|
||||||
|
// Log.Debug(e, "Error in message exchange");
|
||||||
|
// }
|
||||||
|
// Log.Debug("Terminated message exchange, will reuse the pipe");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// catch (Exception ex)
|
||||||
|
// {
|
||||||
|
// Log.Debug(ex, "Error in message exchange flow");
|
||||||
|
// }
|
||||||
|
// }).Start();
|
||||||
|
//}
|
||||||
|
|
||||||
|
//private void ConsumePipe(NamedPipeServerStream inPipe)
|
||||||
|
//{
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// Log.Debug("Initiate message handshake");
|
||||||
|
// var clientPipeName = Encoding.UTF8.GetString(NativeMessageSerializer.ReadMessageBytes(inPipe));
|
||||||
|
// Log.Debug("Client pipe: " + clientPipeName);
|
||||||
|
// if (clientPipeName.StartsWith("XDM-APP-"))
|
||||||
|
// {
|
||||||
|
// var command = NativeMessageSerializer.ReadMessageBytes(inPipe);
|
||||||
|
// var args = ArgsProcessor.ParseArgs(Encoding.UTF8.GetString(command).Split('\r'));
|
||||||
|
// ArgsProcessor.Process(app, args);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// var outPipe = new NamedPipeClientStream(".", clientPipeName, PipeDirection.Out);
|
||||||
|
// outPipe.Connect();
|
||||||
|
// SendConfig(outPipe);
|
||||||
|
// inOutMap[inPipe] = outPipe;
|
||||||
|
// Log.Debug("Message handshake completed");
|
||||||
|
// while (true)
|
||||||
|
// {
|
||||||
|
// var text = NativeMessageSerializer.ReadMessageBytes(inPipe);
|
||||||
|
// using var ms = new MemoryStream(text);
|
||||||
|
// using var br = new BinaryReader(ms);
|
||||||
|
// // Log.Debug("{Text}", text);
|
||||||
|
// var envelop = RawBrowserMessageEnvelop.Deserialize(br);
|
||||||
|
// BrowserMessageHandler.Handle(app, envelop);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// finally
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// NamedPipeClientStream? op = null;
|
||||||
|
// lock (this)
|
||||||
|
// {
|
||||||
|
// if (inOutMap.TryGetValue(inPipe, out op))
|
||||||
|
// {
|
||||||
|
// inOutMap.Remove(inPipe);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// op?.Close();
|
||||||
|
// op?.Dispose();
|
||||||
|
// }
|
||||||
|
// catch { }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//private void SendConfig(Stream pipe)
|
||||||
|
//{
|
||||||
|
// var bytes = GetSyncBytes(app);
|
||||||
|
// NativeMessageSerializer.WriteMessage(pipe, bytes);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//private static void ReadFully(Stream stream, byte[] buf, int bytesToRead)
|
||||||
|
//{
|
||||||
|
// var rem = bytesToRead;
|
||||||
|
// var index = 0;
|
||||||
|
// while (rem > 0)
|
||||||
|
// {
|
||||||
|
// var c = stream.Read(buf, index, rem);
|
||||||
|
// if (c == 0) throw new IOException("Unexpected EOF");
|
||||||
|
// index += c;
|
||||||
|
// rem -= c;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//private static byte[] ReadMessageBytes(Stream pipe)
|
||||||
|
//{
|
||||||
|
// var b4 = new byte[4];
|
||||||
|
// ReadFully(pipe, b4, 4);
|
||||||
|
// var syncLength = BitConverter.ToInt32(b4, 0);
|
||||||
|
// if (syncLength > 4 * 8196)
|
||||||
|
// {
|
||||||
|
// throw new ArgumentException($"Message length too long: {syncLength}");
|
||||||
|
// }
|
||||||
|
// var bytes = new byte[syncLength];
|
||||||
|
// ReadFully(pipe, bytes, syncLength);
|
||||||
|
// return bytes;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//private static void WriteMessage(Stream pipe, string message)
|
||||||
|
//{
|
||||||
|
// var msgBytes = Encoding.UTF8.GetBytes(message);
|
||||||
|
// WriteMessage(pipe, msgBytes);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//private static void WriteMessage(Stream pipe, byte[] msgBytes)
|
||||||
|
//{
|
||||||
|
|
||||||
|
// var bytes = BitConverter.GetBytes(msgBytes.Length);
|
||||||
|
// pipe.Write(bytes, 0, bytes.Length);
|
||||||
|
// pipe.Write(msgBytes, 0, msgBytes.Length);
|
||||||
|
// pipe.Flush();
|
||||||
|
//}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
lock (connectedChannels)
|
||||||
|
{
|
||||||
|
foreach (var channel in connectedChannels)
|
||||||
|
{
|
||||||
|
channel.Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//foreach (var pipe in inPipes)
|
||||||
|
//{
|
||||||
|
// try { pipe.Disconnect(); } catch { }
|
||||||
|
// try { pipe.Dispose(); } catch { }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] GetSyncBytes(IApp app)
|
||||||
|
{
|
||||||
|
var msg = new SyncMessage()
|
||||||
|
{
|
||||||
|
Enabled = Config.Instance.IsBrowserMonitoringEnabled,
|
||||||
|
BlockedHosts = Config.Instance.BlockedHosts,
|
||||||
|
VideoUrls = new string[0],
|
||||||
|
FileExts = Config.Instance.FileExtensions,
|
||||||
|
VidExts = Config.Instance.VideoExtensions,
|
||||||
|
VidList = app.GetVideoList(false).Select(a => new VideoItem
|
||||||
|
{
|
||||||
|
Id = a.ID,
|
||||||
|
Text = a.File,
|
||||||
|
Info = a.DisplayName
|
||||||
|
}).ToList(),
|
||||||
|
MimeList = new string[] { "video", "audio", "mpegurl", "f4m", "m3u8", "dash" },
|
||||||
|
BlockedMimeList = new string[] { "text/javascript", "application/javascript", "text/css", "text/html" },
|
||||||
|
VideoUrlsWithPostReq = new string[] { "ubei/v1/player?key=", "ubei/v1/next?key=" }
|
||||||
|
};
|
||||||
|
return msg.Serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SendArgsToRunningInstance(string[] args)
|
||||||
|
{
|
||||||
|
if (args == null || args.Length < 1) return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var npc =
|
||||||
|
new NamedPipeClientStream(".", PipeName, PipeDirection.Out);
|
||||||
|
npc.Connect();
|
||||||
|
var b = new MemoryStream();
|
||||||
|
var wb = new BinaryWriter(b);
|
||||||
|
wb.Write(Int32.MaxValue);
|
||||||
|
wb.Write(string.Join("\r", args));
|
||||||
|
wb.Close();
|
||||||
|
NativeMessageSerializer.WriteMessage(npc, b.ToArray());
|
||||||
|
npc.Flush();
|
||||||
|
npc.Close();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Debug(ex, ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InstanceAlreadyRunningException : Exception
|
||||||
|
{
|
||||||
|
public InstanceAlreadyRunningException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -86,7 +86,8 @@ namespace BrowserMonitoring
|
||||||
var displayInfo = new StreamingVideoDisplayInfo
|
var displayInfo = new StreamingVideoDisplayInfo
|
||||||
{
|
{
|
||||||
Quality = $"[{fileExt.ToUpperInvariant()}] {size} {item.FormatDescription}",
|
Quality = $"[{fileExt.ToUpperInvariant()}] {size} {item.FormatDescription}",
|
||||||
Size = item.Size
|
Size = item.Size,
|
||||||
|
CreationTime = DateTime.Now
|
||||||
};
|
};
|
||||||
|
|
||||||
//var displayText = $"[{fileExt.ToUpperInvariant()}] {size} {item.FormatDescription}";
|
//var displayText = $"[{fileExt.ToUpperInvariant()}] {size} {item.FormatDescription}";
|
||||||
|
@ -117,7 +118,8 @@ namespace BrowserMonitoring
|
||||||
var displayInfo = new StreamingVideoDisplayInfo
|
var displayInfo = new StreamingVideoDisplayInfo
|
||||||
{
|
{
|
||||||
Quality = $"[{fileExt.ToUpperInvariant()}] {size} {item.FormatDescription}",
|
Quality = $"[{fileExt.ToUpperInvariant()}] {size} {item.FormatDescription}",
|
||||||
Size = item.Size
|
Size = item.Size,
|
||||||
|
CreationTime = DateTime.Now
|
||||||
};
|
};
|
||||||
|
|
||||||
list.Add((Info: mediaItem, DisplayInfo: displayInfo));
|
list.Add((Info: mediaItem, DisplayInfo: displayInfo));
|
||||||
|
@ -175,7 +177,8 @@ namespace BrowserMonitoring
|
||||||
Log.Debug("Display text dash: " + displayText);
|
Log.Debug("Display text dash: " + displayText);
|
||||||
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
||||||
{
|
{
|
||||||
Quality = displayText
|
Quality = displayText,
|
||||||
|
CreationTime = DateTime.Now
|
||||||
}, mediaItem);
|
}, mediaItem);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -197,7 +200,8 @@ namespace BrowserMonitoring
|
||||||
Log.Debug("Display text hls: " + displayText);
|
Log.Debug("Display text hls: " + displayText);
|
||||||
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
||||||
{
|
{
|
||||||
Quality = displayText
|
Quality = displayText,
|
||||||
|
CreationTime = DateTime.Now
|
||||||
}, mediaItem);
|
}, mediaItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -235,7 +239,8 @@ namespace BrowserMonitoring
|
||||||
Log.Debug("Display text hls: " + displayText);
|
Log.Debug("Display text hls: " + displayText);
|
||||||
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
||||||
{
|
{
|
||||||
Quality = displayText
|
Quality = displayText,
|
||||||
|
CreationTime = DateTime.Now
|
||||||
}, mediaItem);
|
}, mediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,7 +314,8 @@ namespace BrowserMonitoring
|
||||||
Log.Debug("Display text hls: " + plc.Quality);
|
Log.Debug("Display text hls: " + plc.Quality);
|
||||||
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
||||||
{
|
{
|
||||||
Quality = displayText
|
Quality = displayText,
|
||||||
|
CreationTime = DateTime.Now
|
||||||
}, video);
|
}, video);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -336,7 +342,8 @@ namespace BrowserMonitoring
|
||||||
var displayText = $"[{container}]";
|
var displayText = $"[{container}]";
|
||||||
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
||||||
{
|
{
|
||||||
Quality = displayText
|
Quality = displayText,
|
||||||
|
CreationTime = DateTime.Now
|
||||||
}, video);
|
}, video);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -432,22 +439,27 @@ namespace BrowserMonitoring
|
||||||
|
|
||||||
if (AddToQueue(info))
|
if (AddToQueue(info))
|
||||||
{
|
{
|
||||||
|
if (!info.IsVideo && mime.StartsWith("audio/"))
|
||||||
|
{
|
||||||
|
HandleDashAudio(info, app, message);
|
||||||
|
}
|
||||||
var di = GetDASHPair(info);
|
var di = GetDASHPair(info);
|
||||||
if (di == null)
|
if (di == null)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
var video = new DualSourceHTTPDownloadInfo
|
var video = CreateDualSourceHTTPDownloadInfo(di, info, message);
|
||||||
{
|
// new DualSourceHTTPDownloadInfo
|
||||||
Uri1 = di.Url,
|
//{
|
||||||
Uri2 = info.Url,
|
// Uri1 = di.Url,
|
||||||
Headers1 = di.Headers,
|
// Uri2 = info.Url,
|
||||||
Headers2 = info.Headers,
|
// Headers1 = di.Headers,
|
||||||
File = Helpers.SanitizeFileName(message.File ?? Helpers.GetFileName(new Uri(message.Url))) + ".mkv",
|
// Headers2 = info.Headers,
|
||||||
Cookies1 = di.Cookies,
|
// File = Helpers.SanitizeFileName(message.File ?? Helpers.GetFileName(new Uri(message.Url))) + ".mkv",
|
||||||
Cookies2 = info.Cookies,
|
// Cookies1 = di.Cookies,
|
||||||
ContentLength = di.Length + info.Length
|
// Cookies2 = info.Cookies,
|
||||||
};
|
// ContentLength = di.Length + info.Length
|
||||||
|
//};
|
||||||
|
|
||||||
var size = di.Length + info.Length;
|
var size = di.Length + info.Length;
|
||||||
Log.Debug("Itag: " + info.ITag + " " + di.ITag);
|
Log.Debug("Itag: " + info.ITag + " " + di.ITag);
|
||||||
|
@ -457,7 +469,8 @@ namespace BrowserMonitoring
|
||||||
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
||||||
{
|
{
|
||||||
Quality = displayText,
|
Quality = displayText,
|
||||||
Size = size
|
Size = size,
|
||||||
|
CreationTime = DateTime.Now
|
||||||
}, video);
|
}, video);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,6 +480,62 @@ namespace BrowserMonitoring
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void HandleDashAudio(DashInfo info, IApp app, Message message)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var size = info.Length;
|
||||||
|
Log.Debug("Itag: " + info.ITag + " " + info.ITag);
|
||||||
|
var name = Helpers.GetFileName(new Uri(info.Url));
|
||||||
|
var ext = Path.GetExtension(name);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(ext))
|
||||||
|
{
|
||||||
|
ext = info.Mime.Contains("webm") ? ".webm" : info.Mime.Contains("mp4") ? ".mp4" : "mkv";
|
||||||
|
}
|
||||||
|
|
||||||
|
var quality = ext.Substring(1)?.ToUpperInvariant();
|
||||||
|
var displayText = $"[{quality} AUDIO] {(size > 0 ? Helpers.FormatSize(size) : string.Empty)}";
|
||||||
|
|
||||||
|
var video = new SingleSourceHTTPDownloadInfo
|
||||||
|
{
|
||||||
|
Uri = info.Url,
|
||||||
|
Headers = info.Headers,
|
||||||
|
File = Helpers.SanitizeFileName(message.File ?? Helpers.GetFileName(new Uri(message.Url))) + ext,
|
||||||
|
Cookies = info.Cookies,
|
||||||
|
ContentLength = size,
|
||||||
|
ContentType = info.Mime
|
||||||
|
};
|
||||||
|
|
||||||
|
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
||||||
|
{
|
||||||
|
Quality = displayText,
|
||||||
|
Size = size,
|
||||||
|
CreationTime = DateTime.Now
|
||||||
|
}, video);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Debug(ex, ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DualSourceHTTPDownloadInfo CreateDualSourceHTTPDownloadInfo(DashInfo info1, DashInfo info2, Message message)
|
||||||
|
{
|
||||||
|
var (video, audio) = info1.IsVideo ? (info1, info2) : (info2, info1);
|
||||||
|
return new DualSourceHTTPDownloadInfo
|
||||||
|
{
|
||||||
|
Uri1 = video.Url,
|
||||||
|
Uri2 = audio.Url,
|
||||||
|
Headers1 = video.Headers,
|
||||||
|
Headers2 = audio.Headers,
|
||||||
|
File = Helpers.SanitizeFileName(message.File ?? Helpers.GetFileName(new Uri(message.Url))) + ".mkv",
|
||||||
|
Cookies1 = video.Cookies,
|
||||||
|
Cookies2 = audio.Cookies,
|
||||||
|
ContentLength = video.Length + audio.Length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
internal static void ProcessNormalVideo(Message message2, IApp app)
|
internal static void ProcessNormalVideo(Message message2, IApp app)
|
||||||
{
|
{
|
||||||
if (IsMediaFragment(message2.GetRequestHeaderFirstValue("Referer")))
|
if (IsMediaFragment(message2.GetRequestHeaderFirstValue("Referer")))
|
||||||
|
@ -530,7 +599,8 @@ namespace BrowserMonitoring
|
||||||
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
||||||
{
|
{
|
||||||
Quality = displayText,
|
Quality = displayText,
|
||||||
Size = size
|
Size = size,
|
||||||
|
CreationTime = DateTime.Now
|
||||||
}, video); ;
|
}, video); ;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace TraceLog
|
||||||
|
{
|
||||||
|
public static class Log
|
||||||
|
{
|
||||||
|
public static void InitFileBasedTrace(string logfile)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Trace.WriteLine("Log init...");
|
||||||
|
//Trace.Listeners.RemoveAt(0);
|
||||||
|
Trace.Listeners.Add(new TextWriterTraceListener(logfile, "myListener"));
|
||||||
|
Trace.AutoFlush = true;
|
||||||
|
Trace.WriteLine("Log init...");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.WriteLine(ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Debug(object obj, string message)
|
||||||
|
{
|
||||||
|
Trace.WriteLine($"[xdm-{DateTime.Now.ToLongTimeString()}] {message} : {obj}");
|
||||||
|
//Trace.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Debug(string message)
|
||||||
|
{
|
||||||
|
Trace.WriteLine($"[xdm-{DateTime.Now.ToLongTimeString()}] {message}");
|
||||||
|
//Trace.Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
Language files are stored in $(SOLUTION)\Lang directory
|
|
@ -0,0 +1,55 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Translations
|
||||||
|
{
|
||||||
|
public static class TextResource
|
||||||
|
{
|
||||||
|
private static Dictionary<string, string> texts = new();
|
||||||
|
|
||||||
|
static TextResource()
|
||||||
|
{
|
||||||
|
Load("English.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Load(string language)
|
||||||
|
{
|
||||||
|
var file = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Path.Combine("Lang", $"{language}"));
|
||||||
|
if (File.Exists(file))
|
||||||
|
{
|
||||||
|
LoadTexts(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetText(string key)
|
||||||
|
{
|
||||||
|
if (texts.TryGetValue(key, out string? label) && label != null)
|
||||||
|
{
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LoadTexts(string path)
|
||||||
|
{
|
||||||
|
var lines = File.ReadAllLines(path);
|
||||||
|
foreach (var line in lines)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(line))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var index = line.IndexOf('=');
|
||||||
|
var key = line.Substring(0, index);
|
||||||
|
var val = line.Substring(index + 1);
|
||||||
|
texts[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<string> GetKeys()
|
||||||
|
{
|
||||||
|
return texts.Keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,196 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<MSBuildAllProjects Condition="'$(MSBuildVersion)' == '' Or '$(MSBuildVersion)' < '16.0'">$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||||
|
<HasSharedItems>true</HasSharedItems>
|
||||||
|
<SharedGUID>5bbea202-65d2-481a-b457-6d8fcc8970d7</SharedGUID>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Label="Configuration">
|
||||||
|
<Import_RootNamespace>XDM.App.Core</Import_RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)BrowserMonitoring\BrowserMessageHandler.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)BrowserMonitoring\BrowserMonitor.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)BrowserMonitoring\IpcHttpHandler.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)BrowserMonitoring\JsonMessageParser.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)BrowserMonitoring\NativeMessagingConfigurer.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)BrowserMonitoring\NativeMessagingHostChannel.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)BrowserMonitoring\NativeMessagingHostHandler.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)BrowserMonitoring\VideoUrlHelper.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)HttpServer\HttpParser.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)HttpServer\HttpServer.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)HttpServer\LineReader.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)HttpServer\RequestContext.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)HttpServer\RequestContextEventArgs.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)HttpServer\ResponseStatus.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)MediaParser\Dash\DashUtil.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)MediaParser\Dash\MpdParser.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)MediaParser\Dash\Representation.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)MediaParser\Hls\HlsHelper.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)MediaParser\Hls\HlsMediaSegment.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)MediaParser\Hls\HlsParser.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)MediaParser\Hls\HlsPlaylist.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)MediaParser\Hls\HlsPlaylistContainer.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)MediaParser\Util\UrlResolver.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)MediaParser\YouTube\VideoFormatData.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)MediaParser\YouTube\YoutubeDataFormatParser.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)NetFX.Polyfill\BlockingCollection.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)NetFX.Polyfill\DictionaryExtensions.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)NetFX.Polyfill\GroupCollectionExtension.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)NetFX.Polyfill\ProcessStartInfoHelper.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)NetFX.Polyfill\StreamExtension.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)NetFX.Polyfill\TupleElementNamesAttribute.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)NetFX.Polyfill\ValueTuplePolyfill.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)TraceLog\Log.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)Translations\TextResource.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Common.UI\BatchDownloadViewController.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Common.UI\CommonUtils.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Common.UI\ComponentUpdaterUI.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Common.UI\DownloadCompleteDialogHelper.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Common.UI\DownloadSelectionViewController.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Common.UI\IconMap.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Common.UI\LinkRefreshDialogHelper.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Common.UI\NewDownloadDialogHelper.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Common.UI\NewVideoDownloadDialogHelper.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Common.UI\VideoDownloaderController.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Interop.CURL\CurlNative.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Interop.WinHttp\Interop.SafeWinHttpHandle.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Interop.WinHttp\Interop.winhttp.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Interop.WinHttp\Interop.winhttp_types.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\CurlHttpClient.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\CurlResponseStream.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\CurlSession.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\DotNetHttpClient.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\DotNetHttpSession.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\HttpClientFactory.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\HttpRequest.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\HttpResponse.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\HttpSession.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\IHttpClient.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\IHttpClientFactory.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\IHttpSession.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\NetFxHttpClient.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\NetFxHttpSession.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\ProxyHelper.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\WebRequestExtensions.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\WinHttpClient.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\WinHttpResponseStream.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\WinHttpSession.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\WinInetClient.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\WinInetProxyHelper.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\WinInetResponseStream.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Clients\Http\WinInetSession.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\ArgsProcessor.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\AuthenticationInfo.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\CancelFlag.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\Category.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\Collections\GenericOrderedDictionary.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\Config.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\DownloadEntries.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\DownloadQueue.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\DownloadSchedule.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\ErrorCode.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\Exceptions.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\GenericEventArgs.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\IApp.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\IAppUI.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\IClipboardMonitor.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\IDownloadCompleteDialog.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\IFileSelectable.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\IListUI.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\INewDownloadDialogSkeleton.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\INewVideoDownloadDialog.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\IProgressWindow.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\IRefreshLinkDialogSkeleton.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\ITextResource.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\IUpdater.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\IUpdaterUI.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\Message.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\PasswordEntry.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\ProxyInfo.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\ProxyInfoSerializer.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\ShortState.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\StreamingVideoDisplayInfo.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\TransactedIO.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Updater\UpdateChecker.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Common\UrlConstants.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\DataAccess\AppDB.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\DataAccess\DownloadList.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\DataAccess\Extensions\DataReaderExtensions.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\DataAccess\DataImportExport.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\DataAccess\SchemaInitializer.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Adaptive\CancelRequestor.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Adaptive\Dash\MultiSourceDASHDownloader.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Adaptive\Dash\MultiSourceDASHDownloadInfo.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Adaptive\Hls\MultiSourceHLSDownloader.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Adaptive\Hls\MultiSourceHLSDownloadInfo.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Adaptive\HttpChunkDownloader.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Adaptive\ICancelRequster.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Adaptive\IChunkStreamMap.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Adaptive\MultiSourceDownloaderBase.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Adaptive\SimpleStreamMap.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Chunk.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\CountdownLatch.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\DownloadFailedEventArgs.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\DownloadStateStore.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\DownloadTypes.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\FileNameFetchMode.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\IBaseDownloader.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\MediaProcessor\BaseMediaProcessor.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\MediaProcessor\FFmpegMediaProcessor.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\ProbeResult.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Progressive\DualHttp\DualSourceHTTPDownloader.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Progressive\DualHttp\DualSourceHTTPDownloadInfo.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Progressive\HTTPDownloaderBase.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Progressive\IPieceCallback.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Progressive\Piece.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Progressive\PieceGrabber.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Progressive\SegmentState.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Progressive\SingleHttp\SingleSourceHTTPDownloader.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\Progressive\SingleHttp\SingleSourceHTTPDownloadInfo.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\ProgressResultEventArgs.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Downloader\SpeedLimiter.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\UI\DownloadLaterEventArgs.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\UI\DownloadListEventArgs.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\UI\FileBrowsedEventArgs.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\UI\IAppWinPeer.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\UI\IBatchDownloadView.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\UI\IButton.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\UI\IDownloadSelectionView.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\UI\IFinishedDownloadRow.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\UI\IInProgressDownloadRow.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\UI\IMenuItem.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\UI\IQueueSelectionDialog.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\UI\IQueuesWindow.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\UI\ISettingsPage.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\UI\IVideoDownloadView.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\UI\QueueListEventArgs.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\UI\QueueSelectionEventArgs.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Util\BrowserLauncher.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Util\Helpers.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Util\SerializationHelper.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Util\TimeHelper.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Util\Win32NativeMethods.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDMApp\AppWin.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDMApp\CallbackActions.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDMApp\ErrorMessages.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDMApp\IconResource.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDMApp\ImportExport.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDMApp\NewDownloadPromptTracker.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDMApp\QueueManager.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDMApp\QueueWindowManager.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDMApp\RemixIcon.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDMApp\Scheduler.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDMApp\UIActions.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)XDMApp\XDMApp.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)YDLWrapper\Structs.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)YDLWrapper\YDLOutputParser.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)YDLWrapper\YDLProcess.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="$(MSBuildThisFileDirectory)Translations\ReadME.txt" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="$(MSBuildThisFileDirectory)XDM.Core\Lib\Updater\" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>5bbea202-65d2-481a-b457-6d8fcc8970d7</ProjectGuid>
|
||||||
|
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
|
||||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
|
||||||
|
<PropertyGroup />
|
||||||
|
<Import Project="XDM.App.Core.projitems" Label="Shared" />
|
||||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
|
||||||
|
</Project>
|
|
@ -0,0 +1,165 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using TraceLog;
|
||||||
|
using Translations;
|
||||||
|
using XDM.Core.Lib.Common;
|
||||||
|
using XDM.Core.Lib.Downloader;
|
||||||
|
using XDM.Core.Lib.Downloader.Progressive.SingleHttp;
|
||||||
|
using XDM.Core.Lib.UI;
|
||||||
|
|
||||||
|
namespace XDM.Common.UI
|
||||||
|
{
|
||||||
|
public class BatchDownloadViewController
|
||||||
|
{
|
||||||
|
private IBatchDownloadView view;
|
||||||
|
public IAppUI AppUI { get; set; }
|
||||||
|
public IApp App { get; set; }
|
||||||
|
public int BatchSize { get; private set; } = 0;
|
||||||
|
|
||||||
|
public BatchDownloadViewController(IBatchDownloadView view, IApp app, IAppUI appUI)
|
||||||
|
{
|
||||||
|
this.view = view;
|
||||||
|
this.AppUI = appUI;
|
||||||
|
this.App = app;
|
||||||
|
|
||||||
|
var arr = new string[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
|
||||||
|
this.view.SetStartLetterRange(arr);
|
||||||
|
this.view.SetEndLetterRange(arr);
|
||||||
|
this.view.IsLetterMode = true;
|
||||||
|
|
||||||
|
this.view.PatternChanged += View_PatternChanged;
|
||||||
|
this.view.OkClicked += View_OkClicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void View_OkClicked(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
OnOKClicked();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void View_PatternChanged(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
OnBatchPatternChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
this.view.ShowWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnOKClicked()
|
||||||
|
{
|
||||||
|
if (this.view.IsBatchMode)
|
||||||
|
{
|
||||||
|
var links = GenerateBatchLink()?.Select(x => (object)new SingleSourceHTTPDownloadInfo { Uri = x.ToString() });
|
||||||
|
if (links == null || !links.Any())
|
||||||
|
{
|
||||||
|
AppUI.ShowMessageBox(this.view, TextResource.GetText("BAT_SELECT_ITEMS"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.view.DestroyWindow();
|
||||||
|
AppUI.ShowDownloadSelectionWindow(FileNameFetchMode.FileNameAndExtension, links);
|
||||||
|
//var dsvc = new DownloadSelectionViewController(this.view.CreateDownloadSelectionView(),
|
||||||
|
// App, AppUI, FileNameFetchMode.FileNameAndExtension, links);
|
||||||
|
//dsvc.Run();
|
||||||
|
//var window = new DownloadSelectionWindow(App, AppUI, Core.Lib.Downloader.FileNameFetchMode.FileNameAndExtension, links);
|
||||||
|
//this.Close();
|
||||||
|
//window.Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBatchPatternChange()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
view.BatchAddress1 = view.BatchAddress2 = view.BatchAddressN = string.Empty;
|
||||||
|
var c = 0;
|
||||||
|
var last = string.Empty;
|
||||||
|
BatchSize = 0;
|
||||||
|
foreach (var url in GenerateBatchLink())
|
||||||
|
{
|
||||||
|
if (c == 0)
|
||||||
|
{
|
||||||
|
view.BatchAddress1 = url.ToString();
|
||||||
|
}
|
||||||
|
else if (c == 1)
|
||||||
|
{
|
||||||
|
view.BatchAddress2 = url.ToString();
|
||||||
|
}
|
||||||
|
last = url.ToString();
|
||||||
|
c++;
|
||||||
|
BatchSize++;
|
||||||
|
}
|
||||||
|
if (c > 1)
|
||||||
|
{
|
||||||
|
view.BatchAddressN = last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (UriFormatException)
|
||||||
|
{
|
||||||
|
AppUI?.ShowMessageBox(this.view, TextResource.GetText("MSG_INVALID_URL"));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Debug(ex, "Error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Uri> GenerateBatchLink()
|
||||||
|
{
|
||||||
|
if (!this.view.Url.Contains('*')) return Enumerable.Empty<Uri>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return GenerateBatchLink(this.view.Url);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Debug(ex, "Error generating batch links");
|
||||||
|
return Enumerable.Empty<Uri>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<Uri> GenerateBatchLink(string url)
|
||||||
|
{
|
||||||
|
var list = new List<Uri>();
|
||||||
|
if (this.view.IsLetterMode)
|
||||||
|
{
|
||||||
|
if (!(this.view.StartLetter.HasValue && this.view.EndLetter.HasValue)) throw new ArgumentException();
|
||||||
|
var startChar = this.view.StartLetter.Value;
|
||||||
|
var endChar = this.view.EndLetter.Value;
|
||||||
|
|
||||||
|
if (startChar >= endChar)
|
||||||
|
{
|
||||||
|
Log.Debug("startChar >= endChar");
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = startChar; i <= endChar; i++)
|
||||||
|
{
|
||||||
|
list.Add(new Uri(url.Replace('*', i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var startNum = this.view.StartNumber;// ParseIntSafe(TxtNumberFrom.Text);
|
||||||
|
var endNum = this.view.EndNumber; //ParseIntSafe(TxtNumberTo.Text);
|
||||||
|
|
||||||
|
if (startNum >= endNum)
|
||||||
|
{
|
||||||
|
Log.Debug("startNum >= endNum");
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = startNum; i <= endNum; i++)
|
||||||
|
{
|
||||||
|
var s = url.Replace("*",
|
||||||
|
this.view.IsUsingLeadingZero ? i.ToString($"D{this.view.LeadingZeroCount}") :
|
||||||
|
i.ToString());
|
||||||
|
list.Add(new Uri(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ using TraceLog;
|
||||||
using Translations;
|
using Translations;
|
||||||
using XDM.Core.Lib.Common;
|
using XDM.Core.Lib.Common;
|
||||||
using XDM.Core.Lib.UI;
|
using XDM.Core.Lib.UI;
|
||||||
|
using XDM.Core.Lib.Util;
|
||||||
|
|
||||||
namespace XDM.Common.UI
|
namespace XDM.Common.UI
|
||||||
{
|
{
|
||||||
|
@ -46,21 +47,18 @@ namespace XDM.Common.UI
|
||||||
|
|
||||||
internal static void OnFileBrowsed(object? sender, FileBrowsedEventArgs args)
|
internal static void OnFileBrowsed(object? sender, FileBrowsedEventArgs args)
|
||||||
{
|
{
|
||||||
var file = Path.GetFileName(args.SelectedFile);
|
var folder = args.SelectedFile;
|
||||||
var folder = Path.GetDirectoryName(args.SelectedFile)!;
|
if (string.IsNullOrEmpty(folder))
|
||||||
if (!Config.Instance.RecentFolders.Contains(folder))
|
|
||||||
{
|
{
|
||||||
Config.Instance.RecentFolders.Insert(0, folder);
|
return;
|
||||||
}
|
}
|
||||||
|
Helpers.UpdateRecentFolderList(folder);
|
||||||
if (sender != null)
|
if (sender != null)
|
||||||
{
|
{
|
||||||
var fileSelectable = (IFileSelectable)sender;
|
var fileSelectable = (IFileSelectable)sender;
|
||||||
fileSelectable.SelectedFileName = file;
|
|
||||||
fileSelectable.SetFolderValues(GetFolderValues());
|
fileSelectable.SetFolderValues(GetFolderValues());
|
||||||
fileSelectable.SeletedFolderIndex = 2;
|
fileSelectable.SeletedFolderIndex = 2;
|
||||||
}
|
}
|
||||||
Config.Instance.FolderSelectionMode = FolderSelectionMode.Manual;
|
|
||||||
Config.SaveConfig();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void OnDropdownSelectionChanged(object? sender, FileBrowsedEventArgs args)
|
internal static void OnDropdownSelectionChanged(object? sender, FileBrowsedEventArgs args)
|
||||||
|
@ -73,13 +71,14 @@ namespace XDM.Common.UI
|
||||||
{
|
{
|
||||||
Config.Instance.FolderSelectionMode = FolderSelectionMode.Auto;
|
Config.Instance.FolderSelectionMode = FolderSelectionMode.Auto;
|
||||||
}
|
}
|
||||||
else
|
else if (!string.IsNullOrEmpty(args.SelectedFile))
|
||||||
{
|
{
|
||||||
Config.Instance.FolderSelectionMode = FolderSelectionMode.Manual;
|
Config.Instance.FolderSelectionMode = FolderSelectionMode.Manual;
|
||||||
if (index != 1)
|
if (index > 1)
|
||||||
{
|
{
|
||||||
Config.Instance.RecentFolders.Remove(args.SelectedFile);
|
Config.Instance.UserSelectedDownloadFolder = args.SelectedFile;
|
||||||
Config.Instance.RecentFolders.Insert(0, args.SelectedFile);
|
//Config.Instance.RecentFolders.Remove(args.SelectedFile);
|
||||||
|
//Config.Instance.RecentFolders.Insert(0, args.SelectedFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Config.SaveConfig();
|
Config.SaveConfig();
|
||||||
|
@ -94,11 +93,8 @@ namespace XDM.Common.UI
|
||||||
Log.Debug($"Index value {index} is invalid for {Config.Instance.FolderSelectionMode}");
|
Log.Debug($"Index value {index} is invalid for {Config.Instance.FolderSelectionMode}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (index - 2 < Config.Instance.RecentFolders.Count)
|
return Config.Instance.RecentFolders.Count > 0 ? Config.Instance.RecentFolders[index - 2] :
|
||||||
{
|
Config.Instance.DefaultDownloadFolder;
|
||||||
return Config.Instance.RecentFolders[index - 2];
|
|
||||||
}
|
|
||||||
return Config.Instance.DefaultDownloadFolder;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -51,7 +51,7 @@ namespace XDM.Common.UI
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
updaterUI.Inderminate = true;
|
updaterUI.Inderminate = true;
|
||||||
if (!UpdateChecker.GetAppUpdates(app.AppVerion, out IList<UpdateInfo> updates, out _, this.updateMode))
|
if (!UpdateChecker.GetAppUpdates(app.AppVerion, out updates, out _, this.updateMode))
|
||||||
{
|
{
|
||||||
updaterUI.DownloadFailed(this, new DownloadFailedEventArgs(ErrorCode.Generic));
|
updaterUI.DownloadFailed(this, new DownloadFailedEventArgs(ErrorCode.Generic));
|
||||||
}
|
}
|
|
@ -11,11 +11,21 @@ namespace XDM.Common.UI
|
||||||
dwnCmpldDlg.FolderText = folder;
|
dwnCmpldDlg.FolderText = folder;
|
||||||
dwnCmpldDlg.FileOpenClicked += (sender, args) =>
|
dwnCmpldDlg.FileOpenClicked += (sender, args) =>
|
||||||
{
|
{
|
||||||
Helpers.OpenFile(args.Path);
|
if (!string.IsNullOrEmpty(args.Path))
|
||||||
|
{
|
||||||
|
Helpers.OpenFile(args.Path!);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
dwnCmpldDlg.FolderOpenClicked += (sender, args) =>
|
dwnCmpldDlg.FolderOpenClicked += (sender, args) =>
|
||||||
{
|
{
|
||||||
Helpers.OpenFolder(args.Path, args.FileName);
|
if (!string.IsNullOrEmpty(args.Path))
|
||||||
|
{
|
||||||
|
Helpers.OpenFolder(args.Path!, args.FileName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
dwnCmpldDlg.DontShowAgainClickd += (sender, args) =>
|
||||||
|
{
|
||||||
|
Config.Instance.ShowDownloadCompleteWindow = false;
|
||||||
};
|
};
|
||||||
dwnCmpldDlg.ShowDownloadCompleteDialog();
|
dwnCmpldDlg.ShowDownloadCompleteDialog();
|
||||||
}
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Translations;
|
||||||
|
using XDM.Core.Lib.Common;
|
||||||
|
using XDM.Core.Lib.Downloader;
|
||||||
|
using XDM.Core.Lib.Downloader.Adaptive.Dash;
|
||||||
|
using XDM.Core.Lib.Downloader.Adaptive.Hls;
|
||||||
|
using XDM.Core.Lib.Downloader.Progressive.DualHttp;
|
||||||
|
using XDM.Core.Lib.Downloader.Progressive.SingleHttp;
|
||||||
|
using XDM.Core.Lib.UI;
|
||||||
|
using XDM.Core.Lib.Util;
|
||||||
|
|
||||||
|
namespace XDM.Common.UI
|
||||||
|
{
|
||||||
|
public class DownloadSelectionViewController
|
||||||
|
{
|
||||||
|
private IDownloadSelectionView view;
|
||||||
|
private FileNameFetchMode mode;
|
||||||
|
|
||||||
|
public IAppUI AppUI { get; set; }
|
||||||
|
public IApp App { get; set; }
|
||||||
|
|
||||||
|
public DownloadSelectionViewController(IDownloadSelectionView view,
|
||||||
|
IApp app, IAppUI appUI,
|
||||||
|
FileNameFetchMode mode, IEnumerable<object> downloads)
|
||||||
|
{
|
||||||
|
this.view = view;
|
||||||
|
App = app;
|
||||||
|
AppUI = appUI;
|
||||||
|
this.mode = mode;
|
||||||
|
|
||||||
|
string? folder = null;
|
||||||
|
if (Config.Instance.FolderSelectionMode == FolderSelectionMode.Manual)
|
||||||
|
{
|
||||||
|
folder = Helpers.GetManualDownloadFolder();
|
||||||
|
}
|
||||||
|
view.DownloadLocation = folder ?? Config.Instance.DefaultDownloadFolder;
|
||||||
|
view.SetData(mode, downloads, PopuplateEntryWrapper);
|
||||||
|
|
||||||
|
view.BrowseClicked += (_, _) =>
|
||||||
|
{
|
||||||
|
var folder = view.SelectFolder();
|
||||||
|
if (!string.IsNullOrEmpty(folder))
|
||||||
|
{
|
||||||
|
view.DownloadLocation = folder;
|
||||||
|
Config.Instance.UserSelectedDownloadFolder = folder;
|
||||||
|
Helpers.UpdateRecentFolderList(folder);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
view.DownloadClicked += View_DownloadClicked; ;
|
||||||
|
view.DownloadLaterClicked += View_DownloadLaterClicked;
|
||||||
|
view.QueueSchedulerClicked += (s, e) =>
|
||||||
|
{
|
||||||
|
appUI.ShowQueueWindow(view);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
this.view.ShowWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void View_DownloadLaterClicked(object? sender, DownloadLaterEventArgs e)
|
||||||
|
{
|
||||||
|
DownloadSelectedItems(false, e.QueueId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void View_DownloadClicked(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
DownloadSelectedItems(true, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool PopuplateEntryWrapper(object obj, IDownloadEntryWrapper entry)
|
||||||
|
{
|
||||||
|
if (obj is SingleSourceHTTPDownloadInfo shi)
|
||||||
|
{
|
||||||
|
entry.EntryType = "Http";
|
||||||
|
entry.Name = shi.File ?? Helpers.GetFileName(new Uri(shi.Uri));
|
||||||
|
}
|
||||||
|
else if (obj is DualSourceHTTPDownloadInfo dhi)
|
||||||
|
{
|
||||||
|
entry.EntryType = "Dash";
|
||||||
|
entry.Name = dhi.File ?? Helpers.GetFileName(new Uri(dhi.Uri1));
|
||||||
|
}
|
||||||
|
else if (obj is MultiSourceHLSDownloadInfo mhi)
|
||||||
|
{
|
||||||
|
entry.EntryType = "Hls";
|
||||||
|
entry.Name = mhi.File ?? Helpers.GetFileName(new Uri(mhi.VideoUri));
|
||||||
|
}
|
||||||
|
else if (obj is MultiSourceDASHDownloadInfo mdi)
|
||||||
|
{
|
||||||
|
entry.EntryType = "MpegDash";
|
||||||
|
entry.Name = mdi.File ?? Helpers.GetFileName(new Uri(mdi.Url));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
entry.DownloadEntry = obj;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddDownload(IDownloadEntryWrapper wrapper, bool startImmediately, string? queueId)
|
||||||
|
{
|
||||||
|
App.SubmitDownload(
|
||||||
|
wrapper.DownloadEntry,
|
||||||
|
wrapper.Name,
|
||||||
|
mode,
|
||||||
|
view.DownloadLocation,
|
||||||
|
startImmediately,
|
||||||
|
view.Authentication,
|
||||||
|
view.Proxy ?? Config.Instance.Proxy,
|
||||||
|
view.EnableSpeedLimit,
|
||||||
|
view.EnableSpeedLimit ? view.SpeedLimit : 0,
|
||||||
|
queueId,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DownloadSelectedItems(bool startImmediately, string? queueId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(view.DownloadLocation))
|
||||||
|
{
|
||||||
|
AppUI.ShowMessageBox(view, TextResource.GetText("MSG_CAT_FOLDER_MISSING"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (view.SelectedRowCount == 0)
|
||||||
|
{
|
||||||
|
AppUI.ShowMessageBox(view, TextResource.GetText("BAT_SELECT_ITEMS"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var item in view.SelectedItems)
|
||||||
|
{
|
||||||
|
if (item.IsSelected)
|
||||||
|
{
|
||||||
|
AddDownload(item, startImmediately, queueId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
view.CloseWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace XDM.Common.UI
|
||||||
|
{
|
||||||
|
public static class IconMap
|
||||||
|
{
|
||||||
|
private static Dictionary<string, HashSet<string>> imageTypes = new()
|
||||||
|
{
|
||||||
|
["CAT_COMPRESSED"] = new HashSet<string> { ".zip", ".gz", ".tar", ".xz", ".7z", ".rar", ".bz2" },
|
||||||
|
["CAT_MUSIC"] = new HashSet<string> { ".mp3", ".aac", ".ac3", ".wma", ".m4a", ".ogg", ".mka" },
|
||||||
|
["CAT_VIDEOS"] = new HashSet<string> { ".mp4", ".mkv", ".ts", ".webm", ".avi", ".divx", ".mov", ".m4v" },
|
||||||
|
["CAT_DOCUMENTS"] = new HashSet<string> { ".docx", ".doc", ".pdf", ".txt", ".xlsx", ".xls", ".html" },
|
||||||
|
["CAT_PROGRAMS"] = new HashSet<string> { ".exe", ".bin", ".appx", ".app", ".msi", ".rpm", ".deb" }
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string GetVectorNameForCategory(string categoryname)
|
||||||
|
{
|
||||||
|
return categoryname switch
|
||||||
|
{
|
||||||
|
"CAT_COMPRESSED" => "ri-file-zip-line",
|
||||||
|
"CAT_MUSIC" => "ri-file-music-line",
|
||||||
|
"CAT_VIDEOS" => "ri-movie-line",
|
||||||
|
"CAT_DOCUMENTS" => "ri-file-text-line",
|
||||||
|
"CAT_PROGRAMS" => "ri-microsoft-line",
|
||||||
|
_ => "ri-file-line",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetFileType(string ext)
|
||||||
|
{
|
||||||
|
foreach (var key in imageTypes.Keys)
|
||||||
|
{
|
||||||
|
var extList = imageTypes[key];
|
||||||
|
if (extList.Contains(ext))
|
||||||
|
{
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "Other";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetVectorNameForFileType(string file)
|
||||||
|
{
|
||||||
|
var ext = Path.GetExtension(file)?.ToLowerInvariant() ?? string.Empty;
|
||||||
|
var fileType = GetFileType(ext);
|
||||||
|
return fileType switch
|
||||||
|
{
|
||||||
|
"CAT_COMPRESSED" => "ri-file-zip-fill",
|
||||||
|
"CAT_MUSIC" => "ri-file-music-fill",
|
||||||
|
"CAT_VIDEOS" => "ri-movie-fill",
|
||||||
|
"CAT_DOCUMENTS" => "ri-file-text-fill",
|
||||||
|
"CAT_PROGRAMS" => "ri-microsoft-fill",
|
||||||
|
_ => "ri-file-fill",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue