Merge branch 'refactoring' into migrate-to-c#

master
subhra74 2022-07-18 22:56:08 +05:30
commit f007befa54
690 changed files with 63518 additions and 52512 deletions

9
.gitignore vendored
View File

@ -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/

4
app/XDM/.editorconfig Normal file
View File

@ -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

View File

@ -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>

View File

@ -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)
{
}
}
}

View File

@ -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;
}
}
}

View File

@ -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>

View File

@ -1,7 +0,0 @@
using System.ComponentModel;
namespace System.Runtime.CompilerServices
{
[EditorBrowsable(EditorBrowsableState.Never)]
internal class IsExternalInit { }
}

View File

@ -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;
}
}
}

View File

@ -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
}
}
}

View File

@ -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;
// }
//}
}

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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 = '\\';
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
// }
// }
//}

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -0,0 +1,2 @@
Clone https://github.com/m-ab-s/media-autobuild_suite
Put ffmpeg_options.txt inside build folder

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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

25
app/XDM/Lang/index.txt Normal file
View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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)
{

View File

@ -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;
}
}

View File

@ -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;
// }
// }
// }
// }
//}

View File

@ -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>

View File

@ -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);
// }
// }
// }
// }
// }
//}

View File

@ -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>

View File

@ -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);
}
}
}

View File

@ -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>

View File

@ -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;
}
}
}

View File

@ -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>

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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
}
}

View File

@ -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;
}
}

View File

@ -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)
{
}
}
}

View File

@ -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); ;
}

View File

@ -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();
}
}
}

View File

@ -0,0 +1 @@
Language files are stored in $(SOLUTION)\Lang directory

View File

@ -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;
}
}
}

View File

@ -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)' &lt; '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>

View File

@ -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>

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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));
}

View File

@ -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();
}

View File

@ -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();
}
}
}

View File

@ -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