Merge branch 'refactoring' into migrate-to-c#
commit
f007befa54
|
@ -348,6 +348,11 @@ MigrationBackup/
|
|||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.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/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
|
||||
LBL_SAVE_IN=Save in
|
||||
ND_IGNORE_URL=Do not capture download from this address
|
||||
ND_MORE=MORE...
|
||||
ND_MORE=More...
|
||||
ND_DOWNLOAD_LATER=Download Later
|
||||
ND_DOWNLOAD_NOW=Download Now
|
||||
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
|
||||
VID_PASTE_URL=Please paste video URL link here
|
||||
SETTINGS_ADV=Advanced settings
|
||||
VID_CHK=Check/Uncheck All
|
||||
O_VID_FMT=Formats
|
||||
VID_CHK=Select all
|
||||
O_VID_FMT=Format
|
||||
BAT_PATTERN=Pattern
|
||||
BAT_LINKS=Links
|
||||
BAT_SELECT_ITEMS=Select items to download
|
||||
|
@ -126,8 +126,8 @@ SETTINGS_GENERAL=General settings
|
|||
SETTINGS_NETWORK=Network settings
|
||||
SETTINGS_CRED=Password manager
|
||||
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_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_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 (Vivaldi, Brave browser etc) or Mozilla based (Icewasel, Waterfox etc) browsers using below links
|
||||
DESC_CHROME=Chromium based browsers
|
||||
DESC_MOZ=Firefox based browsers
|
||||
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_CAT_ADD=Add
|
||||
SETTINGS_CAT_EDIT=Edit
|
||||
DESC_NET1=Connection timeout
|
||||
DESC_NET1=Connection timeout in seconds
|
||||
DESC_NET2=Segments per download
|
||||
NET_MAX_RETRY=Maximum retry limit
|
||||
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_ALREADY_RUNNING=An old version of XDM is already running
|
||||
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">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net5.0;net4.5.2</TargetFrameworks>
|
||||
<Platforms>AnyCPU;x86</Platforms>
|
||||
<TargetFrameworks>net5.0;</TargetFrameworks>
|
||||
<Platforms>AnyCPU;x86;x64</Platforms>
|
||||
<LangVersion>9.0</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net5.0|x86'">
|
||||
<DebugType>none</DebugType>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
|
||||
|
@ -14,8 +19,4 @@
|
|||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net4.5.2' ">
|
||||
<ProjectReference Include="..\CoreFx.Polyfill\CoreFx.Polyfill.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</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>
|
||||
<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>
|
||||
<LangVersion>9.0</LangVersion>
|
||||
<Platforms>AnyCPU;x86</Platforms>
|
||||
<!--<PublishTrimmed>true</PublishTrimmed>
|
||||
<Nullable>enable</Nullable>
|
||||
<Platforms>x86;x64</Platforms>
|
||||
<AssemblyName>xdm-messaging-host</AssemblyName>
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
<TrimMode>Link</TrimMode>
|
||||
<Platforms>AnyCPU;x86</Platforms>
|
||||
<RuntimeIdentifier>win-x86</RuntimeIdentifier>-->
|
||||
<!--<Platforms>AnyCPU;x86</Platforms>-->
|
||||
<!--<RuntimeIdentifiers>win-x86;linux-x64</RuntimeIdentifiers>-->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' != 'net5.0' ">
|
||||
<!--<ItemGroup Condition=" '$(TargetFramework)' != 'net5.0' or '$(TargetFramework)' != 'net6.0' ">
|
||||
<ProjectReference Include="..\NetFX.Polyfill\NetFX.Polyfill.csproj" />
|
||||
</ItemGroup>
|
||||
</ItemGroup>-->
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<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' ">
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net4.7.2' or '$(TargetFramework)' == 'net4.5' ">
|
||||
<AppConfig>App.PostDotNet4.config</AppConfig>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -33,4 +30,11 @@
|
|||
<AppConfig>App.PreDotNet4.config</AppConfig>
|
||||
</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>
|
||||
|
|
|
@ -6,11 +6,11 @@ using System.Text;
|
|||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
using Newtonsoft.Json;
|
||||
using BrowserMonitoring;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using BrowserMonitoring;
|
||||
|
||||
#if NET35
|
||||
using NetFX.Polyfill;
|
||||
using NetFX.Polyfill2;
|
||||
#else
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
|
@ -19,44 +19,66 @@ namespace NativeHost
|
|||
{
|
||||
public class NativeMessagingHostApp
|
||||
{
|
||||
static bool isFirefox = true;
|
||||
static BlockingCollection<byte[]> receivedBrowserMessages = new();
|
||||
static BlockingCollection<byte[]> queuedBrowserMessages = 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)
|
||||
{
|
||||
//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
|
||||
Debug(Environment.Is64BitProcess+"");
|
||||
Debug("Is64BitProcess: " + Environment.Is64BitProcess);
|
||||
#endif
|
||||
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 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(() =>
|
||||
{
|
||||
Debug("t1 reading messages from stdin sent by browser: ");
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
//read from process stdin and write to blocking queue,
|
||||
//they will be sent to xdm once pipe handshake complets
|
||||
var msg = ReadMessageBytes(inputReader);
|
||||
Debug(Encoding.UTF8.GetString(msg));
|
||||
Debug("Waiting for message - stdin...");
|
||||
var msg = NativeMessageSerializer.ReadMessageBytes(inputReader, false);
|
||||
Debug("Reading message from stdin - size: " + msg.Length);
|
||||
Debug("Stdin - " + Encoding.UTF8.GetString(msg));
|
||||
receivedBrowserMessages.Add(JsonToBinary(msg));
|
||||
}
|
||||
}
|
||||
|
@ -77,10 +99,12 @@ namespace NativeHost
|
|||
{
|
||||
//read from blocking queue and write to stdout,
|
||||
//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);
|
||||
Debug("Sending to browser: ");
|
||||
Debug(Encoding.UTF8.GetString(json));
|
||||
WriteMessage(outputWriter, json);
|
||||
NativeMessageSerializer.WriteMessage(outputWriter, json, false);
|
||||
}
|
||||
}
|
||||
catch (Exception exx)
|
||||
|
@ -101,21 +125,38 @@ namespace NativeHost
|
|||
}
|
||||
}
|
||||
|
||||
private static void CreateXDMInstance()
|
||||
private static void CreateXDMInstance(bool minimized = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
Debug("XDM instance creating...1");
|
||||
ProcessStartInfo psi = new()
|
||||
var file = Path.Combine(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ".."),
|
||||
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"),
|
||||
UseShellExecute = true,
|
||||
Arguments = "-m"
|
||||
};
|
||||
var args = minimized ? " -m" : "";
|
||||
if (!NativeProcess.Win32CreateProcess(file, $"\"{file}\"{args}"))
|
||||
{
|
||||
Debug("Win32 create process failed!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessStartInfo psi = new()
|
||||
{
|
||||
FileName = file,
|
||||
UseShellExecute = true
|
||||
};
|
||||
|
||||
Debug("XDM instance creating...");
|
||||
Process.Start(psi);
|
||||
Debug("XDM instance created");
|
||||
if (minimized)
|
||||
{
|
||||
psi.Arguments = "-m";
|
||||
}
|
||||
|
||||
Debug("XDM instance creating...");
|
||||
Process.Start(psi);
|
||||
Debug("XDM instance created");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -125,56 +166,58 @@ namespace NativeHost
|
|||
|
||||
private static void ProcessMessages()
|
||||
{
|
||||
Debug("Log start");
|
||||
Debug("start");
|
||||
|
||||
try
|
||||
{
|
||||
NamedPipeServerStream inPipe = null;
|
||||
NamedPipeClientStream outPipe = null;
|
||||
while (true)
|
||||
//NamedPipeServerStream inPipe = null;
|
||||
NamedPipeClientStream pipe = null;
|
||||
// while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pipeName = Guid.NewGuid().ToString();
|
||||
inPipe = new NamedPipeServerStream(pipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.WriteThrough);
|
||||
//var pipeName = Guid.NewGuid().ToString();
|
||||
//inPipe = new NamedPipeServerStream(pipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.WriteThrough);
|
||||
|
||||
//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");
|
||||
outPipe.Connect();
|
||||
WriteMessage(outPipe, pipeName);
|
||||
Debug("pipename: " + pipeName);
|
||||
pipe.Connect();
|
||||
//NativeMessageSerializer.WriteMessage(outPipe, pipeName);
|
||||
//Debug("pipename: " + pipeName);
|
||||
|
||||
inPipe.WaitForConnection();
|
||||
var syncMsgBytes = ReadMessageBytes(inPipe);
|
||||
Debug("No task message size: " + syncMsgBytes.Length);
|
||||
//inPipe.WaitForConnection();
|
||||
//var syncMsgBytes = NativeMessageSerializer.ReadMessageBytes(inPipe);
|
||||
//Debug("No task message size: " + syncMsgBytes.Length);
|
||||
|
||||
queuedBrowserMessages.Add(syncMsgBytes);
|
||||
//queuedBrowserMessages.Add(syncMsgBytes);
|
||||
|
||||
//handshake with XDM is complete
|
||||
Debug("handshake with XDM is complete");
|
||||
|
||||
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(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var syncMsgBytes = ReadMessageBytes(inPipe);
|
||||
//Debug("Task1 message size: " + syncMsgBytes.Length);
|
||||
var syncMsgBytes = NativeMessageSerializer.ReadMessageBytes(pipe);
|
||||
Debug("Message received from XDM of size: " + syncMsgBytes.Length);
|
||||
if (syncMsgBytes.Length == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
Debug("Message from XDM: " + Encoding.UTF8.GetString(syncMsgBytes));
|
||||
queuedBrowserMessages.Add(syncMsgBytes);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug(ex.ToString());
|
||||
Debug(ex.ToString(), ex);
|
||||
queuedBrowserMessages.Add(Encoding.UTF8.GetBytes("{\"appExited\":\"true\"}"));
|
||||
}
|
||||
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(() =>
|
||||
{
|
||||
try
|
||||
|
@ -190,7 +234,7 @@ namespace NativeHost
|
|||
while (true)
|
||||
{
|
||||
byte[] syncMsgBytes = null;
|
||||
Debug("Task2 reading from browser stdin...");
|
||||
Debug("Task2 reading messages queued by browser...");
|
||||
syncMsgBytes = receivedBrowserMessages.Take();
|
||||
if (syncMsgBytes.Length == 2 && (char)syncMsgBytes[0] == '{' && (char)syncMsgBytes[1] == '}')
|
||||
{
|
||||
|
@ -198,13 +242,14 @@ namespace NativeHost
|
|||
throw new OperationCanceledException("Empty object");
|
||||
}
|
||||
//Debug("Task2 message size fron browser stdin: " + syncMsgBytes.Length);
|
||||
//Debug(Encoding.UTF8.GetString(syncMsgBytes));
|
||||
WriteMessage(outPipe, syncMsgBytes);
|
||||
Debug("Sending message to XDM...");
|
||||
NativeMessageSerializer.WriteMessage(pipe, syncMsgBytes);
|
||||
Debug("Sent message to XDM");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug(ex.ToString());
|
||||
Debug(ex.ToString(), ex);
|
||||
}
|
||||
waitHandle.Set();
|
||||
Debug("Task2 finished");
|
||||
|
@ -220,24 +265,29 @@ namespace NativeHost
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug(ex.ToString());
|
||||
Debug(ex.ToString(), ex);
|
||||
}
|
||||
|
||||
//try
|
||||
//{
|
||||
// inPipe.Disconnect();
|
||||
//}
|
||||
//catch { }
|
||||
try
|
||||
{
|
||||
inPipe.Disconnect();
|
||||
pipe.Close();
|
||||
}
|
||||
catch { }
|
||||
try
|
||||
{
|
||||
outPipe.Dispose();
|
||||
}
|
||||
catch { }
|
||||
try
|
||||
{
|
||||
inPipe.Dispose();
|
||||
pipe.Dispose();
|
||||
}
|
||||
catch { }
|
||||
//try
|
||||
//{
|
||||
// inPipe.Dispose();
|
||||
//}
|
||||
//catch { }
|
||||
}
|
||||
}
|
||||
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);
|
||||
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();
|
||||
Trace.WriteLine($"[xdm-native-messaging-host {DateTime.Now}] {ex2}");
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 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 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()
|
||||
{
|
||||
Load("English");
|
||||
Load("English.txt");
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
LoadTexts(file);
|
||||
|
@ -46,5 +46,10 @@ namespace Translations
|
|||
texts[key] = val;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetKeys()
|
||||
{
|
||||
return texts.Keys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,14 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net3.5;net4.5;net4.7.2;net5.0</TargetFrameworks>
|
||||
<Platforms>AnyCPU;x86</Platforms>
|
||||
<Platforms>AnyCPU;x86;x64</Platforms>
|
||||
<LangVersion>9.0</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net4.7.2|x86'">
|
||||
<DebugType>none</DebugType>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -32,10 +32,17 @@ namespace BrowserMonitoring
|
|||
return;
|
||||
}
|
||||
|
||||
var rawMessage = envelop.Message;
|
||||
if (rawMessage == null)
|
||||
if (envelop.MessageType == "custom")
|
||||
{
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -50,6 +57,17 @@ namespace BrowserMonitoring
|
|||
}
|
||||
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":
|
||||
{
|
||||
var message = Parse(rawMessage);
|
|
@ -40,7 +40,6 @@ namespace BrowserMonitoring
|
|||
Log.Debug(ex.ToString());
|
||||
app.AppUI.ShowMessageBox(null, TextResource.GetText("MSG_ALREADY_RUNNING"));
|
||||
}
|
||||
|
||||
}).Start();
|
||||
}
|
||||
|
||||
|
@ -63,46 +62,59 @@ namespace BrowserMonitoring
|
|||
switch (context.RequestPath)
|
||||
{
|
||||
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);
|
||||
}
|
||||
break;
|
||||
case "/video":
|
||||
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")))
|
||||
var text = Encoding.UTF8.GetString(context.RequestBody!);
|
||||
Log.Debug(text);
|
||||
var message = Message.ParseMessage(text);
|
||||
if (!(Helpers.IsBlockedHost(message.Url) || Helpers.IsCompressedJSorCSS(message.Url)))
|
||||
{
|
||||
VideoUrlHelper.ProcessNormalVideo(message2, app);
|
||||
app.AddDownload(message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "/item":
|
||||
foreach (var item in Encoding.UTF8.GetString(context.RequestBody!).Split(new char[] { '\r', '\n' }))
|
||||
case "/video":
|
||||
{
|
||||
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":
|
||||
app.ClearVideoList();
|
||||
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
|
||||
{
|
||||
Quality = $"[{fileExt.ToUpperInvariant()}] {size} {item.FormatDescription}",
|
||||
Size = item.Size
|
||||
Size = item.Size,
|
||||
CreationTime = DateTime.Now
|
||||
};
|
||||
|
||||
//var displayText = $"[{fileExt.ToUpperInvariant()}] {size} {item.FormatDescription}";
|
||||
|
@ -117,7 +118,8 @@ namespace BrowserMonitoring
|
|||
var displayInfo = new StreamingVideoDisplayInfo
|
||||
{
|
||||
Quality = $"[{fileExt.ToUpperInvariant()}] {size} {item.FormatDescription}",
|
||||
Size = item.Size
|
||||
Size = item.Size,
|
||||
CreationTime = DateTime.Now
|
||||
};
|
||||
|
||||
list.Add((Info: mediaItem, DisplayInfo: displayInfo));
|
||||
|
@ -175,7 +177,8 @@ namespace BrowserMonitoring
|
|||
Log.Debug("Display text dash: " + displayText);
|
||||
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
||||
{
|
||||
Quality = displayText
|
||||
Quality = displayText,
|
||||
CreationTime = DateTime.Now
|
||||
}, mediaItem);
|
||||
}
|
||||
else
|
||||
|
@ -197,7 +200,8 @@ namespace BrowserMonitoring
|
|||
Log.Debug("Display text hls: " + displayText);
|
||||
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
||||
{
|
||||
Quality = displayText
|
||||
Quality = displayText,
|
||||
CreationTime = DateTime.Now
|
||||
}, mediaItem);
|
||||
}
|
||||
}
|
||||
|
@ -235,7 +239,8 @@ namespace BrowserMonitoring
|
|||
Log.Debug("Display text hls: " + displayText);
|
||||
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
||||
{
|
||||
Quality = displayText
|
||||
Quality = displayText,
|
||||
CreationTime = DateTime.Now
|
||||
}, mediaItem);
|
||||
}
|
||||
|
||||
|
@ -309,7 +314,8 @@ namespace BrowserMonitoring
|
|||
Log.Debug("Display text hls: " + plc.Quality);
|
||||
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
||||
{
|
||||
Quality = displayText
|
||||
Quality = displayText,
|
||||
CreationTime = DateTime.Now
|
||||
}, video);
|
||||
}
|
||||
}
|
||||
|
@ -336,7 +342,8 @@ namespace BrowserMonitoring
|
|||
var displayText = $"[{container}]";
|
||||
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
||||
{
|
||||
Quality = displayText
|
||||
Quality = displayText,
|
||||
CreationTime = DateTime.Now
|
||||
}, video);
|
||||
}
|
||||
}
|
||||
|
@ -432,22 +439,27 @@ namespace BrowserMonitoring
|
|||
|
||||
if (AddToQueue(info))
|
||||
{
|
||||
if (!info.IsVideo && mime.StartsWith("audio/"))
|
||||
{
|
||||
HandleDashAudio(info, app, message);
|
||||
}
|
||||
var di = GetDASHPair(info);
|
||||
if (di == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
var video = new DualSourceHTTPDownloadInfo
|
||||
{
|
||||
Uri1 = di.Url,
|
||||
Uri2 = info.Url,
|
||||
Headers1 = di.Headers,
|
||||
Headers2 = info.Headers,
|
||||
File = Helpers.SanitizeFileName(message.File ?? Helpers.GetFileName(new Uri(message.Url))) + ".mkv",
|
||||
Cookies1 = di.Cookies,
|
||||
Cookies2 = info.Cookies,
|
||||
ContentLength = di.Length + info.Length
|
||||
};
|
||||
var video = CreateDualSourceHTTPDownloadInfo(di, info, message);
|
||||
// new DualSourceHTTPDownloadInfo
|
||||
//{
|
||||
// Uri1 = di.Url,
|
||||
// Uri2 = info.Url,
|
||||
// Headers1 = di.Headers,
|
||||
// Headers2 = info.Headers,
|
||||
// File = Helpers.SanitizeFileName(message.File ?? Helpers.GetFileName(new Uri(message.Url))) + ".mkv",
|
||||
// Cookies1 = di.Cookies,
|
||||
// Cookies2 = info.Cookies,
|
||||
// ContentLength = di.Length + info.Length
|
||||
//};
|
||||
|
||||
var size = di.Length + info.Length;
|
||||
Log.Debug("Itag: " + info.ITag + " " + di.ITag);
|
||||
|
@ -457,7 +469,8 @@ namespace BrowserMonitoring
|
|||
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
||||
{
|
||||
Quality = displayText,
|
||||
Size = size
|
||||
Size = size,
|
||||
CreationTime = DateTime.Now
|
||||
}, video);
|
||||
}
|
||||
|
||||
|
@ -467,6 +480,62 @@ namespace BrowserMonitoring
|
|||
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)
|
||||
{
|
||||
if (IsMediaFragment(message2.GetRequestHeaderFirstValue("Referer")))
|
||||
|
@ -530,7 +599,8 @@ namespace BrowserMonitoring
|
|||
app.AddVideoNotification(new StreamingVideoDisplayInfo
|
||||
{
|
||||
Quality = displayText,
|
||||
Size = size
|
||||
Size = size,
|
||||
CreationTime = DateTime.Now
|
||||
}, 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 XDM.Core.Lib.Common;
|
||||
using XDM.Core.Lib.UI;
|
||||
using XDM.Core.Lib.Util;
|
||||
|
||||
namespace XDM.Common.UI
|
||||
{
|
||||
|
@ -46,21 +47,18 @@ namespace XDM.Common.UI
|
|||
|
||||
internal static void OnFileBrowsed(object? sender, FileBrowsedEventArgs args)
|
||||
{
|
||||
var file = Path.GetFileName(args.SelectedFile);
|
||||
var folder = Path.GetDirectoryName(args.SelectedFile)!;
|
||||
if (!Config.Instance.RecentFolders.Contains(folder))
|
||||
var folder = args.SelectedFile;
|
||||
if (string.IsNullOrEmpty(folder))
|
||||
{
|
||||
Config.Instance.RecentFolders.Insert(0, folder);
|
||||
return;
|
||||
}
|
||||
Helpers.UpdateRecentFolderList(folder);
|
||||
if (sender != null)
|
||||
{
|
||||
var fileSelectable = (IFileSelectable)sender;
|
||||
fileSelectable.SelectedFileName = file;
|
||||
fileSelectable.SetFolderValues(GetFolderValues());
|
||||
fileSelectable.SeletedFolderIndex = 2;
|
||||
}
|
||||
Config.Instance.FolderSelectionMode = FolderSelectionMode.Manual;
|
||||
Config.SaveConfig();
|
||||
}
|
||||
|
||||
internal static void OnDropdownSelectionChanged(object? sender, FileBrowsedEventArgs args)
|
||||
|
@ -73,13 +71,14 @@ namespace XDM.Common.UI
|
|||
{
|
||||
Config.Instance.FolderSelectionMode = FolderSelectionMode.Auto;
|
||||
}
|
||||
else
|
||||
else if (!string.IsNullOrEmpty(args.SelectedFile))
|
||||
{
|
||||
Config.Instance.FolderSelectionMode = FolderSelectionMode.Manual;
|
||||
if (index != 1)
|
||||
if (index > 1)
|
||||
{
|
||||
Config.Instance.RecentFolders.Remove(args.SelectedFile);
|
||||
Config.Instance.RecentFolders.Insert(0, args.SelectedFile);
|
||||
Config.Instance.UserSelectedDownloadFolder = args.SelectedFile;
|
||||
//Config.Instance.RecentFolders.Remove(args.SelectedFile);
|
||||
//Config.Instance.RecentFolders.Insert(0, args.SelectedFile);
|
||||
}
|
||||
}
|
||||
Config.SaveConfig();
|
||||
|
@ -94,11 +93,8 @@ namespace XDM.Common.UI
|
|||
Log.Debug($"Index value {index} is invalid for {Config.Instance.FolderSelectionMode}");
|
||||
return null;
|
||||
}
|
||||
if (index - 2 < Config.Instance.RecentFolders.Count)
|
||||
{
|
||||
return Config.Instance.RecentFolders[index - 2];
|
||||
}
|
||||
return Config.Instance.DefaultDownloadFolder;
|
||||
return Config.Instance.RecentFolders.Count > 0 ? Config.Instance.RecentFolders[index - 2] :
|
||||
Config.Instance.DefaultDownloadFolder;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -51,7 +51,7 @@ namespace XDM.Common.UI
|
|||
try
|
||||
{
|
||||
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));
|
||||
}
|
|
@ -11,11 +11,21 @@ namespace XDM.Common.UI
|
|||
dwnCmpldDlg.FolderText = folder;
|
||||
dwnCmpldDlg.FileOpenClicked += (sender, args) =>
|
||||
{
|
||||
Helpers.OpenFile(args.Path);
|
||||
if (!string.IsNullOrEmpty(args.Path))
|
||||
{
|
||||
Helpers.OpenFile(args.Path!);
|
||||
}
|
||||
};
|
||||
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();
|
||||
}
|
|
@ -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