添加项目文件。

flyself 4 years ago
parent af5c453337
commit 67e7f38f9d

@ -0,0 +1,2 @@
!runtimes/x64
!runtimes/x86

@ -0,0 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net45;netstandard2.0</TargetFrameworks>
<Version>2.1.1.0</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>Brotli.NET</PackageId>
<Authors>Jinjun Xie</Authors>
<Company>Jinjun Xie</Company>
<Copyright>Copyright Jinjun Xie 2016</Copyright>
<PackageLicenseUrl></PackageLicenseUrl>
<PackageProjectUrl>https://github.com/XieJJ99/brotli.net</PackageProjectUrl>
<PackageTags>Brotli Compress Decompress .NET Standard Stream</PackageTags>
<PackageReleaseNotes>Add async stream support, update brotli core to v1.0.9</PackageReleaseNotes>
<Description>Supported on dotnet standard2(Windows/Linux/OSX), provide similar interface to Google offical API.Quality and window control is supported.
The library use the native runtime and its performance should be better than System.IO.Compress.BrotliStream.
For more document,please visit https://github.com/XieJJ99/brotli.net.</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<AssemblyVersion>2.1.1.0</AssemblyVersion>
</PropertyGroup>
<ItemGroup>
<!-- <None Update="runtimes\**" Pack="true" PackagePath="." /> -->
<None Include="runtimes\**">
<PackagePath>runtimes</PackagePath>
<Pack>true</Pack>
</None>
</ItemGroup>
<Import Project="build\Brotli.NET.targets" />
<ItemGroup>
<None Remove="build\Brotli.NET.targets~RF6be2dbfa.TMP" />
</ItemGroup>
<ItemGroup>
<!-- <None Update="runtimes\**" Pack="true" PackagePath="." /> -->
<!-- <None Include="runtimes\**\*.dll">
<PackagePath>build\runtimes</PackagePath>
<Pack>true</Pack>
</None> -->
<None Include="build\Brotli.NET.targets">
<PackagePath>build</PackagePath>
<Pack>true</Pack>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" Condition=" '$(TargetFramework)' == 'net45' " />
</ItemGroup>
</Project>

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Brotli
{
public enum BrotliDecoderParameter:int
{
DisableRingBufferReallocation=0,
LargeWindow=1,
}
}

@ -0,0 +1,22 @@
namespace Brotli
{
public enum BrotliDecoderResult:int
{
/// <summary>
/// Decoding error, e.g. corrupt input or memory allocation problem
/// </summary>
Error = 0,
/// <summary>
/// Decoding successfully completed
/// </summary>
Success = 1,
/// <summary>
/// Partially done; should be called again with more input
/// </summary>
NeedsMoreInput = 2,
/// <summary>
/// Partially done; should be called again with more output
/// </summary>
NeedsMoreOutput = 3
};
}

@ -0,0 +1,26 @@
namespace Brotli
{
public enum BrotliEncoderOperation : int
{
Process = 0,
/// <summary>
/// Request output stream to flush. Performed when input stream is depleted
/// and there is enough space in output stream.
/// </summary>
Flush = 1,
/// <summary>
/// Request output stream to finish. Performed when input stream is depleted
/// and there is enough space in output stream.
/// </summary>
Finish = 2,
/// <summary>
/// Emits metadata block to stream. Stream is soft-flushed before metadata
/// block is emitted. CAUTION: when operation is started, length of the input
/// buffer is interpreted as length of a metadata block; changing operation,
/// expanding or truncating input before metadata block is completely emitted
/// will cause an error; metadata block must not be greater than 16MiB.
/// </summary>
EmitMetadata = 3
};
}

@ -0,0 +1,22 @@
namespace Brotli
{
public enum BrotliEncoderParameter : int
{
Mode = 0,
/// <summary>
/// Controls the compression-speed vs compression-density tradeoffs. The higher
/// the quality, the slower the compression. Range is 0 to 11.
/// </summary>
Quality = 1,
/// <summary>
/// Base 2 logarithm of the sliding window size. Range is 10 to 24.
/// </summary>
LGWin = 2,
/// <summary>
/// Base 2 logarithm of the maximum input block size. Range is 16 to 24.
/// If set to 0, the value will be set based on the quality.
/// </summary>
LGBlock = 3
};
}

@ -0,0 +1,25 @@
using System;
namespace Brotli
{
public class BrotliDecodeException : BrotliException
{
public int Code { get; set; }
public String ErrorText { get; set; }
public BrotliDecodeException(int code, String errorText) : base() {
Code = code;
ErrorText = errorText;
}
public BrotliDecodeException(String message, int code, String errorText) : base(message) {
Code = code;
ErrorText = errorText;
}
public BrotliDecodeException(String message, Exception innerException, int code, String errorText) : base(message, innerException) {
Code = code;
ErrorText = errorText;
}
}
}

@ -0,0 +1,11 @@
using System;
namespace Brotli
{
public class BrotliException:Exception
{
public BrotliException() : base() { }
public BrotliException(String message) : base(message) { }
public BrotliException(String message, Exception innerException) : base(message, innerException) { }
}
}

@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Brotli
{
public static class BrotliExtensions
{
/// <summary>
/// Compress the data with brotli
/// </summary>
/// <param name="rawData">inputData</param>
/// <param name="quality">quality,0~11</param>
/// <param name="window">compress window(10~24)</param>
/// <returns>compressed bytes</returns>
public static byte[] CompressToBrotli(this byte[] rawData, uint quality = 5, uint window = 22)
{
if (rawData == null) throw new ArgumentNullException(nameof(rawData));
using (var msInput = new System.IO.MemoryStream(rawData))
{
return CompressToBrotli(msInput, quality, window);
}
}
/// <summary>
/// Compress the data with brotli
/// </summary>
/// <param name="inStream">input stream</param>
/// <param name="quality">quality,0~11</param>
/// <param name="window">compress window(10~24)</param>
/// <returns>compressed bytes</returns>
public static byte[] CompressToBrotli(this System.IO.Stream inStream, uint quality = 5, uint window = 22)
{
using (System.IO.MemoryStream msOutput = new System.IO.MemoryStream())
{
CompressToBrotli(inStream, msOutput, quality, window);
var output = msOutput.ToArray();
return output;
}
}
/// <summary>
/// Compress the data with brotli
/// </summary>
/// <param name="inStream">input stream</param>
/// <param name="destStream">dest output stream</param>
/// <param name="quality">quality,0~11</param>
/// <param name="window">compress window(10~24)</param>
public static void CompressToBrotli(this System.IO.Stream inStream, System.IO.Stream destStream, uint quality = 5, uint window = 22)
{
using (BrotliStream bs = new BrotliStream(destStream, System.IO.Compression.CompressionMode.Compress))
{
bs.SetQuality(quality);
bs.SetWindow(window);
inStream.CopyTo(bs);
bs.Close();
}
}
public static byte[] DecompressFromBrotli(this byte[] rawData)
{
if (rawData == null) throw new ArgumentNullException(nameof(rawData));
using (var msInput = new System.IO.MemoryStream(rawData))
{
return DecompressFromBrotli(msInput);
}
}
public static byte[] DecompressFromBrotli(this System.IO.Stream inStream)
{
using (System.IO.MemoryStream msOutput = new System.IO.MemoryStream())
{
DecompressFromBrotli(inStream, msOutput);
var output = msOutput.ToArray();
return output;
}
}
public static void DecompressFromBrotli(this System.IO.Stream inStream,System.IO.Stream destStream)
{
using (BrotliStream bs = new BrotliStream(inStream, System.IO.Compression.CompressionMode.Decompress))
{
bs.CopyTo(destStream);
destStream.Flush();
}
}
}
}

@ -0,0 +1,67 @@
using System;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
namespace Brotli
{
internal class AsyncHelper
{
private static readonly TaskFactory _myTaskFactory = new
TaskFactory(CancellationToken.None, TaskCreationOptions.None,
TaskContinuationOptions.None, TaskScheduler.Default);
internal static void RunSync(Func<Task> func, bool await = false)
{
CultureInfo cultureUi = CultureInfo.CurrentUICulture;
CultureInfo culture = CultureInfo.CurrentCulture;
_myTaskFactory.StartNew(delegate
{
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = cultureUi;
return func();
}).Unwrap().ConfigureAwait(await).GetAwaiter().GetResult();
}
// Microsoft.AspNet.Identity.AsyncHelper
internal static TResult RunSync<TResult>(Func<Task<TResult>> func, bool await = false)
{
CultureInfo cultureUi = CultureInfo.CurrentUICulture;
CultureInfo culture = CultureInfo.CurrentCulture;
return _myTaskFactory.StartNew<Task<TResult>>(delegate
{
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = cultureUi;
return func();
}).Unwrap<TResult>().ConfigureAwait(await).GetAwaiter().GetResult();
}
internal static TResult RunSync<TResult>(Func<object, Task<TResult>> func, object state, bool await = false)
{
CultureInfo cultureUi = CultureInfo.CurrentUICulture;
CultureInfo culture = CultureInfo.CurrentCulture;
return _myTaskFactory.StartNew<Task<TResult>>(delegate
{
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = cultureUi;
return func(state);
}, state).Unwrap<TResult>().ConfigureAwait(await).GetAwaiter().GetResult();
}
internal static TResult RunSync<TResult>(Func<object, Task<TResult>> func, object state, CancellationToken cancellationToken, bool await = false)
{
CultureInfo cultureUi = CultureInfo.CurrentUICulture;
CultureInfo culture = CultureInfo.CurrentCulture;
return _myTaskFactory.StartNew<Task<TResult>>(delegate
{
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = cultureUi;
return func(state);
}, state, cancellationToken).Unwrap<TResult>().ConfigureAwait(await).GetAwaiter().GetResult();
}
}
}

@ -0,0 +1,316 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Brotli
{
public class Brolib
{
static bool UseX86 = IntPtr.Size == 4;
#region Encoder
public static IntPtr BrotliEncoderCreateInstance()
{
if (UseX86)
{
return Brolib32.BrotliEncoderCreateInstance(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
}
else
{
return Brolib64.BrotliEncoderCreateInstance(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
}
}
public static IntPtr GetModuleHandle(String moduleName)
{
IntPtr r = IntPtr.Zero;
foreach (ProcessModule mod in Process.GetCurrentProcess().Modules)
{
if (String.Compare(mod.ModuleName,moduleName,true)==0)
{
r = mod.BaseAddress;
break;
}
}
return r;
}
public static void FreeLibrary()
{
IntPtr libHandle = IntPtr.Zero;
libHandle = GetModuleHandle(LibPathBootStrapper.LibPath);
if (libHandle!=IntPtr.Zero)
{
NativeLibraryLoader.FreeLibrary(libHandle);
}
}
public static bool BrotliEncoderSetParameter(IntPtr state, BrotliEncoderParameter parameter, UInt32 value)
{
if (UseX86)
{
return Brolib32.BrotliEncoderSetParameter(state, parameter, value);
}
else
{
return Brolib64.BrotliEncoderSetParameter(state, parameter, value);
}
}
//@to rewrite using the following APIs
//BrotliGetDictionary
//BrotliGetTransforms
//BrotliSetDictionaryData
//BrotliTransformDictionaryWord
//public static void BrotliEncoderSetCustomDictionary(IntPtr state, UInt32 size, IntPtr dict)
//{
// if (UseX86)
// {
// Brolib32.BrotliEncoderSetCustomDictionary(state, size, dict);
// }
// else
// {
// Brolib64.BrotliEncoderSetCustomDictionary(state, size, dict);
// }
//}
public static bool BrotliEncoderCompressStream(
IntPtr state, BrotliEncoderOperation op, ref UInt32 availableIn,
ref IntPtr nextIn, ref UInt32 availableOut, ref IntPtr nextOut, out UInt32 totalOut)
{
if (UseX86)
{
return Brolib32.BrotliEncoderCompressStream(state, op, ref availableIn, ref nextIn, ref availableOut, ref nextOut, out totalOut);
}
else
{
UInt64 availableInL = availableIn;
UInt64 availableOutL = availableOut;
UInt64 totalOutL = 0;
var r = Brolib64.BrotliEncoderCompressStream(state, op, ref availableInL, ref nextIn, ref availableOutL, ref nextOut, out totalOutL);
availableIn = (UInt32)availableInL;
availableOut = (UInt32)availableOutL;
totalOut = (UInt32)totalOutL;
return r;
}
}
public static bool BrotliEncoderIsFinished(IntPtr state)
{
if (UseX86)
{
return Brolib32.BrotliEncoderIsFinished(state);
}
else
{
return Brolib64.BrotliEncoderIsFinished(state);
}
}
public static void BrotliEncoderDestroyInstance(IntPtr state)
{
if (UseX86)
{
Brolib32.BrotliEncoderDestroyInstance(state);
}
else
{
Brolib64.BrotliEncoderDestroyInstance(state);
}
}
public static UInt32 BrotliEncoderVersion()
{
if (UseX86)
{
return Brolib32.BrotliEncoderVersion();
}
else
{
return Brolib64.BrotliEncoderVersion();
}
}
public static IntPtr BrotliDecoderTakeOutput(IntPtr state, ref UInt32 size)
{
if (UseX86)
{
return Brolib32.BrotliDecoderTakeOutput(state, ref size);
}
else
{
UInt64 longSize = size;
var r = Brolib64.BrotliDecoderTakeOutput(state, ref longSize);
size = (UInt32)longSize;
return r;
}
}
#endregion
#region Decoder
public static IntPtr BrotliDecoderCreateInstance()
{
if (UseX86)
{
return Brolib32.BrotliDecoderCreateInstance(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
}
else
{
return Brolib64.BrotliDecoderCreateInstance(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
}
}
public static bool BrotliDecoderSetParameter(IntPtr state, BrotliDecoderParameter param, UInt32 value)
{
if (UseX86)
{
return Brolib32.BrotliDecoderSetParameter(state,param,value);
}
else
{
return Brolib64.BrotliDecoderSetParameter(state, param, value);
}
}
//@to rewrite using the following APIs
//BrotliGetDictionary
//BrotliGetTransforms
//BrotliSetDictionaryData
//BrotliTransformDictionaryWord
//public static void BrotliDecoderSetCustomDictionary(IntPtr state, UInt32 size, IntPtr dict)
//{
// if (UseX86)
// {
// Brolib32.BrotliDecoderSetCustomDictionary(state, size, dict);
// }
// else
// {
// Brolib64.BrotliDecoderSetCustomDictionary(state, size, dict);
// }
//}
public static BrotliDecoderResult BrotliDecoderDecompressStream(
IntPtr state, ref UInt32 availableIn,
ref IntPtr nextIn, ref UInt32 availableOut, ref IntPtr nextOut, out UInt32 totalOut)
{
if (UseX86)
{
return Brolib32.BrotliDecoderDecompressStream(state, ref availableIn, ref nextIn, ref availableOut, ref nextOut, out totalOut);
}
else
{
UInt64 availableInL = availableIn;
UInt64 availableOutL = availableOut;
UInt64 totalOutL = 0;
var r = Brolib64.BrotliDecoderDecompressStream(state, ref availableInL, ref nextIn, ref availableOutL, ref nextOut, out totalOutL);
availableIn = (UInt32)availableInL;
availableOut = (UInt32)availableOutL;
totalOut = (UInt32)totalOutL;
return r;
}
}
public static void BrotliDecoderDestroyInstance(IntPtr state)
{
if (UseX86)
{
Brolib32.BrotliDecoderDestroyInstance(state);
}
else
{
Brolib64.BrotliDecoderDestroyInstance(state);
}
}
public static UInt32 BrotliDecoderVersion()
{
if (UseX86)
{
return Brolib32.BrotliDecoderVersion();
}
else
{
return Brolib64.BrotliDecoderVersion();
}
}
public static bool BrotliDecoderIsUsed(IntPtr state)
{
if (UseX86)
{
return Brolib32.BrotliDecoderIsUsed(state);
}
else
{
return Brolib64.BrotliDecoderIsUsed(state);
}
}
public static bool BrotliDecoderIsFinished(IntPtr state)
{
if (UseX86)
{
return Brolib32.BrotliDecoderIsFinished(state);
}
else
{
return Brolib64.BrotliDecoderIsFinished(state);
}
}
public static Int32 BrotliDecoderGetErrorCode(IntPtr state)
{
if (UseX86)
{
return Brolib32.BrotliDecoderGetErrorCode(state);
}
else
{
return Brolib64.BrotliDecoderGetErrorCode(state);
}
}
public static String BrotliDecoderErrorString(Int32 code)
{
IntPtr r = IntPtr.Zero;
if (UseX86)
{
r = Brolib32.BrotliDecoderErrorString(code);
}
else
{
r = Brolib64.BrotliDecoderErrorString(code);
}
if (r != IntPtr.Zero)
{
return Marshal.PtrToStringAnsi(r);
}
return String.Empty;
}
public static IntPtr BrotliEncoderTakeOutput(IntPtr state, ref UInt32 size)
{
if (UseX86)
{
return Brolib32.BrotliEncoderTakeOutput(state, ref size);
}
else
{
UInt64 longSize = size;
var r = Brolib64.BrotliEncoderTakeOutput(state, ref longSize);
size = (UInt32)longSize;
return r;
}
}
#endregion
}
}

@ -0,0 +1,443 @@
using System;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace Brotli
{
public class BrotliStream : Stream
{
const int BufferSize = 64 * 1024;
protected Stream _stream = null;
protected MemoryStream _intermediateStream = new MemoryStream();
protected CompressionMode _mode = CompressionMode.Compress;
protected IntPtr _state = IntPtr.Zero;
protected IntPtr _ptrInputBuffer = IntPtr.Zero;
protected IntPtr _ptrOutputBuffer = IntPtr.Zero;
protected IntPtr _ptrNextInput = IntPtr.Zero;
protected IntPtr _ptrNextOutput = IntPtr.Zero;
protected UInt32 _availableIn = 0;
protected UInt32 _availableOut = BufferSize;
protected Byte[] _managedBuffer;
protected Boolean _endOfStream = false;
protected int _readOffset = 0;
protected BrotliDecoderResult _lastDecodeResult = BrotliDecoderResult.NeedsMoreInput;
protected Boolean _leaveOpen = false;
public BrotliStream(Stream baseStream, CompressionMode mode,bool leaveOpen)
{
if (baseStream == null) throw new ArgumentNullException("baseStream");
_mode = mode;
_stream = baseStream;
_leaveOpen = leaveOpen;
if (_mode == CompressionMode.Compress)
{
_state = Brolib.BrotliEncoderCreateInstance();
if (_state == IntPtr.Zero)
{
throw new BrotliException("Unable to create brotli encoder instance");
}
Brolib.BrotliEncoderSetParameter(_state, BrotliEncoderParameter.Quality, 5);
Brolib.BrotliEncoderSetParameter(_state, BrotliEncoderParameter.LGWin, 22);
}
else
{
_state = Brolib.BrotliDecoderCreateInstance();
if (_state == IntPtr.Zero)
{
throw new BrotliException("Unable to create brotli decoder instance");
}
//follow the brotli default standard
var succ = Brolib.BrotliDecoderSetParameter(_state, BrotliDecoderParameter.LargeWindow, 1U);
if (!succ)
{
throw new BrotliException("failed to set decoder parameter to large window");
}
}
_ptrInputBuffer = Marshal.AllocHGlobal(BufferSize);
_ptrOutputBuffer = Marshal.AllocHGlobal(BufferSize);
_ptrNextInput = _ptrInputBuffer;
_ptrNextOutput = _ptrOutputBuffer;
_managedBuffer = new Byte[BufferSize];
}
public BrotliStream(Stream baseStream, CompressionMode mode):this(baseStream,mode,false)
{
}
/// <summary>
/// Set the compress quality(0~11)
/// </summary>
/// <param name="quality">compress quality</param>
public void SetQuality(uint quality)
{
if (quality < 0 || quality > 11)
{
throw new ArgumentException("quality", "the range of quality is 0~11");
}
Brolib.BrotliEncoderSetParameter(_state, BrotliEncoderParameter.Quality, quality);
}
/// <summary>
/// Set the compress LGWin(10~24)
/// </summary>
/// <param name="window">the window size</param>
public void SetWindow(uint window)
{
if (window < 10 || window > 24)
{
throw new ArgumentException("window", "the range of window is 10~24");
}
Brolib.BrotliEncoderSetParameter(_state, BrotliEncoderParameter.LGWin, window);
}
public override bool CanRead
{
get
{
if (_stream == null)
{
return false;
}
return (_mode == System.IO.Compression.CompressionMode.Decompress && _stream.CanRead);
}
}
public override bool CanSeek
{
get
{
return false;
}
}
public override bool CanWrite
{
get
{
if (_stream == null)
{
return false;
}
return (_mode == System.IO.Compression.CompressionMode.Compress && _stream.CanWrite);
}
}
public override long Length
{
get
{
throw new NotImplementedException();
}
}
public override long Position
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override async Task FlushAsync(CancellationToken cancellationToken)
{
if (_stream == null)
{
throw new ObjectDisposedException(null, "Underlying stream is disposed");
}
if (_mode == CompressionMode.Compress)
{
await FlushBrotliStreamAsync(false).ConfigureAwait(false);
}
}
public override void Flush()
{
AsyncHelper.RunSync(() => FlushAsync());
}
protected virtual async Task FlushBrotliStreamAsync(Boolean finished)
{
//test if the resource has been freed
if (_state == IntPtr.Zero) return;
if (Brolib.BrotliEncoderIsFinished(_state)) return;
BrotliEncoderOperation op = finished ? BrotliEncoderOperation.Finish : BrotliEncoderOperation.Flush;
UInt32 totalOut = 0;
while (true)
{
var compressOK = Brolib.BrotliEncoderCompressStream(_state, op, ref _availableIn, ref _ptrNextInput, ref _availableOut, ref _ptrNextOutput, out totalOut);
if (!compressOK) throw new BrotliException("Unable to finish encode stream");
var extraData = _availableOut != BufferSize;
if (extraData)
{
var bytesWrote = (int)(BufferSize - _availableOut);
Marshal.Copy(_ptrOutputBuffer, _managedBuffer, 0, bytesWrote);
await _stream.WriteAsync(_managedBuffer, 0, bytesWrote).ConfigureAwait(false);
_availableOut = BufferSize;
_ptrNextOutput = _ptrOutputBuffer;
}
if (Brolib.BrotliEncoderIsFinished(_state)) break;
if (!extraData) break;
}
}
protected virtual void FlushBrotliStream(Boolean finished)
{
AsyncHelper.RunSync(() => FlushBrotliStreamAsync(finished));
}
protected override void Dispose(bool disposing)
{
if (_mode == CompressionMode.Compress)
{
FlushBrotliStream(true);
}
base.Dispose(disposing);
if (!_leaveOpen) _stream.Dispose();
_intermediateStream.Dispose();
if (_ptrInputBuffer!=IntPtr.Zero) Marshal.FreeHGlobal(_ptrInputBuffer);
if (_ptrOutputBuffer != IntPtr.Zero) Marshal.FreeHGlobal(_ptrOutputBuffer);
_managedBuffer = null;
_ptrInputBuffer = IntPtr.Zero;
_ptrOutputBuffer = IntPtr.Zero;
if (_state != IntPtr.Zero)
{
if (_mode == CompressionMode.Compress)
{
Brolib.BrotliEncoderDestroyInstance(_state);
}
else
{
Brolib.BrotliDecoderDestroyInstance(_state);
}
_state = IntPtr.Zero;
}
}
public void TruncateBeginning(MemoryStream ms, int numberOfBytesToRemove)
{
#if NETSTANDARD2_0
ArraySegment<byte> buf;
if(ms.TryGetBuffer(out buf))
{
Buffer.BlockCopy(buf.Array, numberOfBytesToRemove, buf.Array, 0, (int)ms.Length - numberOfBytesToRemove);
ms.SetLength(ms.Length - numberOfBytesToRemove);
}
else
{
throw new UnauthorizedAccessException();
}
#else
byte[] buf = ms.GetBuffer();
Buffer.BlockCopy(buf, numberOfBytesToRemove, buf, 0, (int)ms.Length - numberOfBytesToRemove);
ms.SetLength(ms.Length - numberOfBytesToRemove);
#endif
}
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (_mode != CompressionMode.Decompress) throw new BrotliException("Can't read on this stream");
int bytesRead = (int)(_intermediateStream.Length - _readOffset);
uint totalCount = 0;
Boolean endOfStream = false;
Boolean errorDetected = false;
while (bytesRead < count)
{
while (true)
{
if (_lastDecodeResult == BrotliDecoderResult.NeedsMoreInput)
{
_availableIn = (UInt32) await _stream.ReadAsync(_managedBuffer, 0, (int)BufferSize).ConfigureAwait(false);
_ptrNextInput = _ptrInputBuffer;
if (_availableIn <= 0)
{
endOfStream = true;
break;
}
Marshal.Copy(_managedBuffer, 0, _ptrInputBuffer, (int)_availableIn);
}
else if (_lastDecodeResult == BrotliDecoderResult.NeedsMoreOutput)
{
Marshal.Copy(_ptrOutputBuffer, _managedBuffer, 0, BufferSize);
await _intermediateStream.WriteAsync(_managedBuffer, 0, BufferSize).ConfigureAwait(false);
bytesRead += BufferSize;
_availableOut = BufferSize;
_ptrNextOutput = _ptrOutputBuffer;
}
else
{
//Error or OK
endOfStream = true;
break;
}
_lastDecodeResult = Brolib.BrotliDecoderDecompressStream(_state, ref _availableIn, ref _ptrNextInput,
ref _availableOut, ref _ptrNextOutput, out totalCount);
if (bytesRead >= count) break;
}
if (endOfStream && !Brolib.BrotliDecoderIsFinished(_state))
{
errorDetected = true;
}
if (_lastDecodeResult == BrotliDecoderResult.Error || errorDetected)
{
var error = Brolib.BrotliDecoderGetErrorCode(_state);
var text = Brolib.BrotliDecoderErrorString(error);
throw new BrotliDecodeException(String.Format("Unable to decode stream,possibly corrupt data.Code={0}({1})", error, text), error, text);
}
if (endOfStream && !Brolib.BrotliDecoderIsFinished(_state) && _lastDecodeResult == BrotliDecoderResult.NeedsMoreInput)
{
throw new BrotliException("Unable to decode stream,unexpected EOF");
}
if (endOfStream && _ptrNextOutput != _ptrOutputBuffer)
{
int remainBytes = (int)(_ptrNextOutput.ToInt64() - _ptrOutputBuffer.ToInt64());
bytesRead += remainBytes;
Marshal.Copy(_ptrOutputBuffer, _managedBuffer, 0, remainBytes);
await _intermediateStream.WriteAsync(_managedBuffer, 0, remainBytes).ConfigureAwait(false);
_ptrNextOutput = _ptrOutputBuffer;
}
if (endOfStream) break;
}
if (_intermediateStream.Length - _readOffset >= count || endOfStream)
{
_intermediateStream.Seek(_readOffset, SeekOrigin.Begin);
var bytesToRead = (int)(_intermediateStream.Length - _readOffset);
if (bytesToRead > count) bytesToRead = count;
await _intermediateStream.ReadAsync(buffer, offset, bytesToRead).ConfigureAwait(false);
TruncateBeginning(_intermediateStream, _readOffset + bytesToRead);
_readOffset = 0;
return bytesToRead;
}
return 0;
}
public override int Read(byte[] buffer, int offset, int count)
{
async Task<int> task()
{
return await ReadAsync(buffer,offset,count).ConfigureAwait(false);
}
return AsyncHelper.RunSync(task);
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
static int totalWrote = 0;
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (_mode != CompressionMode.Compress) throw new BrotliException("Can't write on this stream");
totalWrote += count;
//Console.WriteLine(String.Format("Write {0} bytes,total={1} bytes.", count, totalWrote));
UInt32 totalOut = 0;
int bytesRemain = count;
int currentOffset = offset;
Boolean compressOK = true;
while (bytesRemain > 0)
{
int copyLen = bytesRemain > BufferSize ? BufferSize : bytesRemain;
Marshal.Copy(buffer, currentOffset, _ptrInputBuffer, copyLen);
bytesRemain -= copyLen;
currentOffset += copyLen;
_availableIn = (UInt32)copyLen;
_ptrNextInput = _ptrInputBuffer;
while (_availableIn > 0)
{
compressOK = Brolib.BrotliEncoderCompressStream(_state, BrotliEncoderOperation.Process, ref _availableIn, ref _ptrNextInput, ref _availableOut,
ref _ptrNextOutput, out totalOut);
if (!compressOK) throw new BrotliException("Unable to compress stream");
if (_availableOut != BufferSize)
{
var bytesWrote = (int)(BufferSize - _availableOut);
//Byte[] localBuffer = new Byte[bytesWrote];
Marshal.Copy(_ptrOutputBuffer, _managedBuffer, 0, bytesWrote);
await _stream.WriteAsync(_managedBuffer, 0, bytesWrote).ConfigureAwait(false);
_availableOut = BufferSize;
_ptrNextOutput = _ptrOutputBuffer;
}
}
if (Brolib.BrotliEncoderIsFinished(_state)) break;
}
}
public override void Write(byte[] buffer, int offset, int count)
{
async Task task()
{
await WriteAsync(buffer,offset,count).ConfigureAwait(false);
}
AsyncHelper.RunSync(task);
}
}
#if NET35
/// <summary>
/// Improve compability issue on FX35
/// </summary>
public static class StreamCopyExtension
{
public static void CopyTo(this Stream source,Stream destination, int bufferSize=4*1024)
{
if (source==null)
{
throw new ArgumentNullException(nameof(source));
}
if (destination==null)
{
throw new ArgumentNullException(nameof(destination));
}
if (!source.CanRead)
{
throw new InvalidOperationException("source stream is not readable");
}
if (!destination.CanWrite)
{
throw new InvalidOperationException("destination stream is not writeable");
}
if (bufferSize<=0)
{
throw new InvalidOperationException("buffer size should be greate than zero");
}
byte[] buffer = new byte[bufferSize];
int read;
while ((read = source.Read(buffer, 0, buffer.Length)) > 0)
destination.Write(buffer, 0, read);
}
}
#endif
}

@ -0,0 +1,62 @@
using System;
using System.Runtime.InteropServices;
namespace Brotli
{
class Brolib32
{
static Brolib32()
{
var path = LibPathBootStrapper.LibPath;
NativeLibraryLoader nl = new NativeLibraryLoader(path);
#region set encoder
nl.FillDelegate(out BrotliEncoderCreateInstance);
nl.FillDelegate(out BrotliEncoderSetParameter);
nl.FillDelegate(out BrotliEncoderCompressStream);
nl.FillDelegate(out BrotliEncoderIsFinished);
nl.FillDelegate(out BrotliEncoderDestroyInstance);
nl.FillDelegate(out BrotliEncoderVersion);
nl.FillDelegate(out BrotliEncoderVersion);
nl.FillDelegate(out BrotliEncoderTakeOutput);
#endregion
#region set decoder
nl.FillDelegate(out BrotliDecoderCreateInstance);
nl.FillDelegate(out BrotliDecoderSetParameter);
nl.FillDelegate(out BrotliDecoderDecompressStream);
nl.FillDelegate(out BrotliDecoderDestroyInstance);
nl.FillDelegate(out BrotliDecoderVersion);
nl.FillDelegate(out BrotliDecoderIsUsed);
nl.FillDelegate(out BrotliDecoderIsFinished);
nl.FillDelegate(out BrotliDecoderGetErrorCode);
nl.FillDelegate(out BrotliDecoderErrorString);
nl.FillDelegate(out BrotliDecoderTakeOutput);
#endregion
}
#region Encoder
internal static Delegate32.BrotliEncoderCreateInstanceDelegate BrotliEncoderCreateInstance;
internal static Delegate32.BrotliEncoderSetParameterDelegate BrotliEncoderSetParameter;
//internal static Delegate32.BrotliEncoderSetCustomDictionaryDelegate BrotliEncoderSetCustomDictionary;
internal static Delegate32.BrotliEncoderCompressStreamDelegate BrotliEncoderCompressStream;
internal static Delegate32.BrotliEncoderIsFinishedDelegate BrotliEncoderIsFinished;
internal static Delegate32.BrotliEncoderDestroyInstanceDelegate BrotliEncoderDestroyInstance;
internal static Delegate32.BrotliEncoderVersionDelegate BrotliEncoderVersion;
internal static Delegate32.BrotliEncoderTakeOutputDelegate BrotliEncoderTakeOutput;
#endregion
#region Decoder
internal static Delegate32.BrotliDecoderCreateInstanceDelegate BrotliDecoderCreateInstance;
internal static Delegate32.BrotliDecoderSetParameter BrotliDecoderSetParameter;
//internal static Delegate32.BrotliDecoderSetCustomDictionary BrotliDecoderSetCustomDictionary;
internal static Delegate32.BrotliDecoderDecompressStreamDelegate BrotliDecoderDecompressStream;
internal static Delegate32.BrotliDecoderDestroyInstanceDelegate BrotliDecoderDestroyInstance;
internal static Delegate32.BrotliDecoderVersionDelegate BrotliDecoderVersion;
internal static Delegate32.BrotliDecoderIsUsedDelegate BrotliDecoderIsUsed;
internal static Delegate32.BrotliDecoderIsFinishedDelegate BrotliDecoderIsFinished;
internal static Delegate32.BrotliDecoderGetErrorCodeDelegate BrotliDecoderGetErrorCode;
internal static Delegate32.BrotliDecoderErrorStringDelegate BrotliDecoderErrorString;
internal static Delegate32.BrotliDecoderTakeOutputDelegate BrotliDecoderTakeOutput;
#endregion
}
}

@ -0,0 +1,62 @@
using System;
using System.Runtime.InteropServices;
namespace Brotli
{
class Brolib64
{
static Brolib64()
{
var path = LibPathBootStrapper.LibPath;
NativeLibraryLoader nl = new NativeLibraryLoader(path);
#region set encoder
nl.FillDelegate(out BrotliEncoderCreateInstance);
nl.FillDelegate(out BrotliEncoderSetParameter);
nl.FillDelegate(out BrotliEncoderCompressStream);
nl.FillDelegate(out BrotliEncoderIsFinished);
nl.FillDelegate(out BrotliEncoderDestroyInstance);
nl.FillDelegate(out BrotliEncoderVersion);
nl.FillDelegate(out BrotliEncoderVersion);
nl.FillDelegate(out BrotliEncoderTakeOutput);
#endregion
#region set decoder
nl.FillDelegate(out BrotliDecoderCreateInstance);
nl.FillDelegate(out BrotliDecoderSetParameter);
nl.FillDelegate(out BrotliDecoderDecompressStream);
nl.FillDelegate(out BrotliDecoderDestroyInstance);
nl.FillDelegate(out BrotliDecoderVersion);
nl.FillDelegate(out BrotliDecoderIsUsed);
nl.FillDelegate(out BrotliDecoderIsFinished);
nl.FillDelegate(out BrotliDecoderGetErrorCode);
nl.FillDelegate(out BrotliDecoderErrorString);
nl.FillDelegate(out BrotliDecoderTakeOutput);
#endregion
}
#region Encoder
internal static Delegate64.BrotliEncoderCreateInstanceDelegate BrotliEncoderCreateInstance;
internal static Delegate64.BrotliEncoderSetParameterDelegate BrotliEncoderSetParameter;
//internal static Delegate64.BrotliEncoderSetCustomDictionaryDelegate BrotliEncoderSetCustomDictionary;
internal static Delegate64.BrotliEncoderCompressStreamDelegate BrotliEncoderCompressStream;
internal static Delegate64.BrotliEncoderIsFinishedDelegate BrotliEncoderIsFinished;
internal static Delegate64.BrotliEncoderDestroyInstanceDelegate BrotliEncoderDestroyInstance;
internal static Delegate64.BrotliEncoderVersionDelegate BrotliEncoderVersion;
internal static Delegate64.BrotliEncoderTakeOutputDelegate BrotliEncoderTakeOutput;
#endregion
#region Decoder
internal static Delegate64.BrotliDecoderCreateInstanceDelegate BrotliDecoderCreateInstance;
internal static Delegate64.BrotliDecoderSetParameter BrotliDecoderSetParameter;
//internal static Delegate64.BrotliDecoderSetCustomDictionary BrotliDecoderSetCustomDictionary;
internal static Delegate64.BrotliDecoderDecompressStreamDelegate BrotliDecoderDecompressStream;
internal static Delegate64.BrotliDecoderDestroyInstanceDelegate BrotliDecoderDestroyInstance;
internal static Delegate64.BrotliDecoderVersionDelegate BrotliDecoderVersion;
internal static Delegate64.BrotliDecoderIsUsedDelegate BrotliDecoderIsUsed;
internal static Delegate64.BrotliDecoderIsFinishedDelegate BrotliDecoderIsFinished;
internal static Delegate64.BrotliDecoderGetErrorCodeDelegate BrotliDecoderGetErrorCode;
internal static Delegate64.BrotliDecoderErrorStringDelegate BrotliDecoderErrorString;
internal static Delegate64.BrotliDecoderTakeOutputDelegate BrotliDecoderTakeOutput;
#endregion
}
}

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace Brotli
{
internal class Delegate32
{
#region Encoder
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr BrotliEncoderCreateInstanceDelegate(IntPtr allocFunc, IntPtr freeFunc, IntPtr opaque);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate bool BrotliEncoderSetParameterDelegate(IntPtr state, BrotliEncoderParameter parameter, UInt32 value);
//delegate void BrotliEncoderSetCustomDictionary(IntPtr state, UInt32 size, IntPtr dict);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate bool BrotliEncoderCompressStreamDelegate(
IntPtr state, BrotliEncoderOperation op, ref UInt32 availableIn,
ref IntPtr nextIn, ref UInt32 availableOut, ref IntPtr nextOut, out UInt32 totalOut);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate bool BrotliEncoderIsFinishedDelegate(IntPtr state);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void BrotliEncoderDestroyInstanceDelegate(IntPtr state);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate UInt32 BrotliEncoderVersionDelegate();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr BrotliEncoderTakeOutputDelegate(IntPtr state, ref UInt32 size);
#endregion
#region Decoder
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr BrotliDecoderCreateInstanceDelegate(IntPtr allocFunc, IntPtr freeFunc, IntPtr opaque);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate bool BrotliDecoderSetParameter(IntPtr state, BrotliDecoderParameter param, UInt32 value);
//delegate void BrotliDecoderSetCustomDictionary(IntPtr state, UInt32 size, IntPtr dict);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate BrotliDecoderResult BrotliDecoderDecompressStreamDelegate(
IntPtr state, ref UInt32 availableIn, ref IntPtr nextIn,
ref UInt32 availableOut, ref IntPtr nextOut, out UInt32 totalOut);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void BrotliDecoderDestroyInstanceDelegate(IntPtr state);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate UInt32 BrotliDecoderVersionDelegate();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate bool BrotliDecoderIsUsedDelegate(IntPtr state);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate bool BrotliDecoderIsFinishedDelegate(IntPtr state);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 BrotliDecoderGetErrorCodeDelegate(IntPtr state);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr BrotliDecoderErrorStringDelegate(Int32 code);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr BrotliDecoderTakeOutputDelegate(IntPtr state, ref UInt32 size);
#endregion
}
}

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace Brotli
{
internal class Delegate64
{
#region Encoder
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr BrotliEncoderCreateInstanceDelegate(IntPtr allocFunc, IntPtr freeFunc, IntPtr opaque);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate bool BrotliEncoderSetParameterDelegate(IntPtr state, BrotliEncoderParameter parameter, UInt32 value);
//delegate void BrotliEncoderSetCustomDictionary(IntPtr state, UInt32 size, IntPtr dict);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate bool BrotliEncoderCompressStreamDelegate(
IntPtr state, BrotliEncoderOperation op, ref UInt64 availableIn,
ref IntPtr nextIn, ref UInt64 availableOut, ref IntPtr nextOut, out UInt64 totalOut);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate bool BrotliEncoderIsFinishedDelegate(IntPtr state);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void BrotliEncoderDestroyInstanceDelegate(IntPtr state);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate UInt32 BrotliEncoderVersionDelegate();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr BrotliEncoderTakeOutputDelegate(IntPtr state, ref UInt64 size);
#endregion
#region Decoder
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr BrotliDecoderCreateInstanceDelegate(IntPtr allocFunc, IntPtr freeFunc, IntPtr opaque);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate bool BrotliDecoderSetParameter(IntPtr state, BrotliDecoderParameter param, UInt32 value);
//delegate void BrotliDecoderSetCustomDictionary(IntPtr state, UInt64 size, IntPtr dict);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate BrotliDecoderResult BrotliDecoderDecompressStreamDelegate(
IntPtr state, ref UInt64 availableIn, ref IntPtr nextIn,
ref UInt64 availableOut, ref IntPtr nextOut, out UInt64 totalOut);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void BrotliDecoderDestroyInstanceDelegate(IntPtr state);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate UInt32 BrotliDecoderVersionDelegate();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate bool BrotliDecoderIsUsedDelegate(IntPtr state);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate bool BrotliDecoderIsFinishedDelegate(IntPtr state);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 BrotliDecoderGetErrorCodeDelegate(IntPtr state);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr BrotliDecoderErrorStringDelegate(Int32 code);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr BrotliDecoderTakeOutputDelegate(IntPtr state, ref UInt64 size);
#endregion
}
}

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
namespace Brotli
{
internal class LibPathBootStrapper
{
internal static string LibPath { get; private set; }
static LibPathBootStrapper()
{
string fileName = null;
if (NativeLibraryLoader.IsWindows)
{
if (NativeLibraryLoader.Is64Bit)
{
fileName = "brolib_x64.dll";
}
else
{
fileName = "brolib_x86.dll";
}
} else if (NativeLibraryLoader.IsLinux)
{
if (NativeLibraryLoader.Is64Bit)
{
fileName = "brolib_x64.so";
}
else
{
fileName = "brolib_x86.so";
}
} else if (NativeLibraryLoader.IsMacOSX)
{
if (NativeLibraryLoader.Is64Bit)
{
fileName = "brolib_x64.dylib";
}
}
if (string.IsNullOrEmpty(fileName)) throw new NotSupportedException($"OS not supported:{Environment.OSVersion.ToString()}");
var paths = NativeLibraryLoader.GetPossibleRuntimeDirectories();
var libFound = false;
foreach(var path in paths)
{
var fullPath = Path.Combine(path, fileName);
if (System.IO.File.Exists(fullPath))
{
LibPath = fullPath;
libFound = true;
break;
}
}
if (!libFound) throw new NotSupportedException($"Unable to find library {fileName}");
}
}
}

@ -0,0 +1,234 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
namespace Brotli
{
internal class NativeLibraryLoader
{
internal static bool IsWindows = false;
internal static bool IsLinux = false;
internal static bool IsMacOSX = false;
internal static bool IsNetCore = false;
internal static bool Is64Bit = false;
static NativeLibraryLoader()
{
#if NET35 || NET40
IsWindows=true;
#else
IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
IsMacOSX = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
IsNetCore = RuntimeInformation.FrameworkDescription.StartsWith(".NET Core");
#endif
if (!IsWindows && !IsLinux && !IsMacOSX)
{
throw new InvalidOperationException("Unsupported platform.");
}
Is64Bit = IntPtr.Size == 8;
}
// dlopen flags
const int RtldLazy = 1;
const int RtldGlobal = 8;
readonly string _libraryPath;
internal IntPtr Handle { get; private set; }
internal NativeLibraryLoader(string libraryPath)
{
_libraryPath = libraryPath;
Handle = LoadLibrary(_libraryPath, out var errorMsg);
if (!String.IsNullOrEmpty(errorMsg))
{
throw new BrotliException($"unable to load library {libraryPath}");
}
}
public static IntPtr GetWin32ModuleHandle(String moduleName)
{
IntPtr r = IntPtr.Zero;
foreach (ProcessModule mod in Process.GetCurrentProcess().Modules)
{
if (mod.ModuleName == moduleName)
{
r = mod.BaseAddress;
break;
}
}
return r;
}
/// <summary>
/// Loads method in the library
/// </summary>
/// <param name="symbolName">The methold of the library</param>
/// <returns>method address</returns>
private IntPtr LoadSymbol(string symbolName)
{
if (IsWindows)
{
return WindowsLoader.GetProcAddress(Handle, symbolName);
}
if (IsLinux)
{
if (IsNetCore)
{
return CoreCLRLoader.dlsym(Handle, symbolName);
}
return LinuxLoader.dlsym(Handle, symbolName);
}
if (IsMacOSX)
{
return MacOSXLoader.dlsym(Handle, symbolName);
}
throw new InvalidOperationException("Unsupported platform.");
}
public void FillDelegate<T>(out T delegateType) where T : class
{
var typeName = typeof(T).Name;
var kt = "Delegate";
if (typeName.EndsWith(kt))
{
typeName = typeName.Substring(0, typeName.Length - kt.Length);
}
delegateType = GetNativeMethodDelegate<T>(typeName);
}
public T GetNativeMethodDelegate<T>(string methodName) where T : class
{
var ptr = LoadSymbol(methodName);
if (ptr == IntPtr.Zero)
{
throw new MissingMethodException(string.Format("The native method \"{0}\" does not exist", methodName));
}
return Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)) as T; // generic version not available in .NET45
}
internal static string[] GetPossibleRuntimeDirectories()
{
#if NET35 || NET40
var assemblyDirectory = Path.GetDirectoryName(typeof(LibPathBootStrapper).Assembly.Location);
#else
var assemblyDirectory = Path.GetDirectoryName(typeof(LibPathBootStrapper).GetTypeInfo().Assembly.Location);
#endif
//var platform = "win";
//if (IsLinux)
//{
// platform= "linux";
//}
//if (IsMacOSX)
//{
// platform= "osx";
//}
//string runtimesDirectory = string.Format("runtimes/{0}/native", platform);
string runtimesDirectory;
if (Is64Bit)
{
runtimesDirectory = "x64"; // string.Format("x64/{0}", platform);
}
else
{
runtimesDirectory = "x86"; // string.Format("x86/{0}", platform);
}
string runtimesFullDirectory = Path.Combine(assemblyDirectory,runtimesDirectory);
var iisBaseDirectory = $"{AppDomain.CurrentDomain.BaseDirectory}bin/{runtimesDirectory}";
var execAssemblyDirectory= System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase).Replace("file:\\","")+"/"+ runtimesDirectory;
#if NET35
var netCoreAppStyleDirectory = Path.Combine(Path.Combine(assemblyDirectory, "../.."), runtimesDirectory);
#else
var netCoreAppStyleDirectory = Path.Combine(assemblyDirectory, "../..", runtimesDirectory);
#endif
string[] paths = new[] { assemblyDirectory, runtimesFullDirectory, runtimesDirectory, netCoreAppStyleDirectory,iisBaseDirectory,execAssemblyDirectory };
paths = paths.Select(v => v.Replace('/', Path.DirectorySeparatorChar)).ToArray();
return paths;
}
internal static bool FreeLibrary(IntPtr handle)
{
string errorMsg = null;
if (IsWindows)
{
return WindowsLoader.FreeLibrary(handle);
}
if (IsLinux)
{
if (IsNetCore)
{
return UnloadLibraryPosix(CoreCLRLoader.dlclose, CoreCLRLoader.dlerror, handle, out errorMsg);
}
return UnloadLibraryPosix(LinuxLoader.dlclose, LinuxLoader.dlerror, handle, out errorMsg);
}
if (IsMacOSX)
{
return UnloadLibraryPosix(MacOSXLoader.dlclose, MacOSXLoader.dlerror, handle, out errorMsg);
}
throw new InvalidOperationException("Unsupported platform.");
}
/// <summary>
/// Load library
/// </summary>
internal static IntPtr LoadLibrary(string libraryPath, out string errorMsg)
{
if (IsWindows)
{
errorMsg = null;
//var handle = GetWin32ModuleHandle(libraryPath);
//if (handle != IntPtr.Zero) return handle;
var handle= WindowsLoader.LoadLibrary(libraryPath);
if (handle== IntPtr.Zero)
{
throw new System.ComponentModel.Win32Exception($"failed to load library {libraryPath}");
}
return handle;
}
if (IsLinux)
{
if (IsNetCore)
{
return LoadLibraryPosix(CoreCLRLoader.dlopen, CoreCLRLoader.dlerror, libraryPath, out errorMsg);
}
return LoadLibraryPosix(LinuxLoader.dlopen, LinuxLoader.dlerror, libraryPath, out errorMsg);
}
if (IsMacOSX)
{
return LoadLibraryPosix(MacOSXLoader.dlopen, MacOSXLoader.dlerror, libraryPath, out errorMsg);
}
throw new InvalidOperationException("Unsupported platform.");
}
static IntPtr LoadLibraryPosix(Func<string, int, IntPtr> dlopenFunc, Func<IntPtr> dlerrorFunc, string libraryPath, out string errorMsg)
{
errorMsg = null;
IntPtr ret = dlopenFunc(libraryPath, RtldGlobal + RtldLazy);
if (ret == IntPtr.Zero)
{
errorMsg = Marshal.PtrToStringAnsi(dlerrorFunc());
}
return ret;
}
static bool UnloadLibraryPosix(Func<IntPtr,int> dlcloseFunc, Func<IntPtr> dlerrorFunc, IntPtr handle,out string errorMsg)
{
errorMsg = null;
var r = dlcloseFunc(handle);
if (r!=0)
{
errorMsg= Marshal.PtrToStringAnsi(dlerrorFunc());
return false;
}
return true;
}
}
}

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace Brotli
{
static class WindowsLoader
{
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr LoadLibrary(string dllFilePath);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
}
static class LinuxLoader
{
[DllImport("libdl.so")]
internal static extern IntPtr dlopen(string filename, int flags);
[DllImport("libdl.so")]
internal static extern IntPtr dlerror();
[DllImport("libdl.so")]
internal static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport("libdl.so")]
internal static extern int dlclose(IntPtr handle);
}
static class MacOSXLoader
{
[DllImport("libSystem.dylib")]
internal static extern IntPtr dlopen(string filename, int flags);
[DllImport("libSystem.dylib")]
internal static extern IntPtr dlerror();
[DllImport("libSystem.dylib")]
internal static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport("libSystem.dylib")]
internal static extern int dlclose(IntPtr handle);
}
/// <summary>
/// Similarly as for Mono on Linux, we load symbols for
/// dlopen and dlsym from the "libcoreclr.so",
/// to avoid the dependency on libc-dev Linux.
/// </summary>
static class CoreCLRLoader
{
[DllImport("libcoreclr.so")]
internal static extern IntPtr dlopen(string filename, int flags);
[DllImport("libcoreclr.so")]
internal static extern IntPtr dlerror();
[DllImport("libcoreclr.so")]
internal static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport("libcoreclr.so")]
internal static extern int dlclose(IntPtr handle);
}
}

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<NativeLibs Include="$(MSBuildThisFileDirectory)..\runtimes\**\*.*" />
<None Include="@(NativeLibs)">
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<!--
<NativePdbs Include="$(MSBuildThisFileDirectory)..\runtimes\**\*.pdb" />
<None Include="@(NativePdbs)" Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('$(AllowedReferenceRelatedFileExtensions)', '\.pdb'))">
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> -->
</Project>

@ -0,0 +1,9 @@
<prism:PrismApplication x:Class="DatabaseManager.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
xmlns:local="clr-namespace:DatabaseManager">
<Application.Resources>
</Application.Resources>
</prism:PrismApplication>

@ -0,0 +1,23 @@
using DatabaseManager.Views;
using Prism.Ioc;
using System.Windows;
namespace DatabaseManager
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App
{
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<ViewCover>("Cover");
containerRegistry.RegisterForNavigation<ViewHeader>("Header");
}
}
}

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{95F12C16-86B5-43D5-AF3E-D2489E2DB239}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DatabaseManager</RootNamespace>
<AssemblyName>DatabaseManager</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="Models\CoverModel.cs" />
<Compile Include="ViewModels\ViewCoverViewModel.cs" />
<Compile Include="ViewModels\ViewHeaderViewModel.cs" />
<Compile Include="Views\ViewCover.xaml.cs">
<DependentUpon>ViewCover.xaml</DependentUpon>
</Compile>
<Compile Include="Views\ViewHeader.xaml.cs">
<DependentUpon>ViewHeader.xaml</DependentUpon>
</Compile>
<Page Include="Views\MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="ViewModels\MainWindowViewModel.cs" />
<Compile Include="Views\MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Page Include="Views\ViewCover.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\ViewHeader.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Prism.DryIoc" Version="8.0.0.1909" />
<PackageReference Include="System.Data.SQLite.Core">
<Version>1.0.112.2</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DownKyi.Core\DownKyi.Core.csproj">
<Project>{4fde0364-f65b-4812-bfe8-34e886624fbd}</Project>
<Name>DownKyi.Core</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

@ -0,0 +1,47 @@
namespace DatabaseManager.Models
{
public class CoverModel
{
public long Avid { get; set; }
public string Bvid { get; set; }
public long Cid { get; set; }
public string Url { get; set; }
public string Md5 { get; set; }
//private long avid;
//public long Avid
//{
// get { return avid; }
// set { SetProperty(ref avid, value); }
//}
//private string bvid;
//public string Bvid
//{
// get { return bvid; }
// set { SetProperty(ref bvid, value); }
//}
//private long cid;
//public long Cid
//{
// get { return cid; }
// set { SetProperty(ref cid, value); }
//}
//private string url;
//public string Url
//{
// get { return url; }
// set { SetProperty(ref url, value); }
//}
//private string md5;
//public string Md5
//{
// get { return md5; }
// set { SetProperty(ref md5, value); }
//}
}
}

@ -0,0 +1,55 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("DatabaseManager")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DatabaseManager")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

@ -0,0 +1,71 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace DatabaseManager.Properties
{
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources
{
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources()
{
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager
{
get
{
if ((resourceMan == null))
{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DatabaseManager.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
}
}

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace DatabaseManager.Properties
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default
{
get
{
return defaultInstance;
}
}
}
}

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

@ -0,0 +1,47 @@
using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
namespace DatabaseManager.ViewModels
{
public class MainWindowViewModel : BindableBase
{
private readonly IRegionManager regionManager;
private string _title = "DatabaseManager";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public MainWindowViewModel(IRegionManager regionManager)
{
this.regionManager = regionManager;
}
private DelegateCommand coverCommand;
public DelegateCommand CoverCommand => coverCommand ?? (coverCommand = new DelegateCommand(ExecuteCoverCommand));
private void ExecuteCoverCommand()
{
regionManager.RequestNavigate("ContentRegion", "Cover");
}
private DelegateCommand headerCommand;
public DelegateCommand HeaderCommand => headerCommand ?? (headerCommand = new DelegateCommand(ExecuteHeaderCommand));
private void ExecuteHeaderCommand()
{
regionManager.RequestNavigate("ContentRegion", "Header");
}
}
}

@ -0,0 +1,66 @@
using DownKyi.Core.Storage.Database;
using Prism.Mvvm;
using Prism.Regions;
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows;
namespace DatabaseManager.ViewModels
{
public class ViewCoverViewModel : BindableBase, INavigationAware
{
private ObservableCollection<Cover> coverList;
public ObservableCollection<Cover> CoverList
{
get { return coverList; }
set { SetProperty(ref coverList, value); }
}
public ViewCoverViewModel()
{
CoverList = new ObservableCollection<Cover>();
}
public async void OnNavigatedTo(NavigationContext navigationContext)
{
await Task.Run(() =>
{
CoverDb coverDb = new CoverDb();
var covers = coverDb.QueryAll();
if (covers == null)
{
return;
}
Application.Current.Dispatcher.Invoke(new Action(() =>
{
CoverList.Clear();
foreach (var cover in covers)
{
//CoverModel newCover = new CoverModel
//{
// Avid = cover.Avid,
// Bvid = cover.Bvid,
// Cid = cover.Cid,
// Url = cover.Url,
// Md5 = cover.Md5
//};
CoverList.Add(cover);
}
}));
});
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
}
}
}

@ -0,0 +1,57 @@
using DownKyi.Core.Storage.Database;
using Prism.Mvvm;
using Prism.Regions;
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows;
namespace DatabaseManager.ViewModels
{
public class ViewHeaderViewModel : BindableBase, INavigationAware
{
private ObservableCollection<Header> headerList;
public ObservableCollection<Header> HeaderList
{
get { return headerList; }
set { SetProperty(ref headerList, value); }
}
public ViewHeaderViewModel()
{
HeaderList = new ObservableCollection<Header>();
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
}
public async void OnNavigatedTo(NavigationContext navigationContext)
{
await Task.Run(() =>
{
HeaderDb headerDb = new HeaderDb();
var headers = headerDb.QueryAll();
if (headers == null)
{
return;
}
Application.Current.Dispatcher.Invoke(new Action(() =>
{
HeaderList.Clear();
foreach (var cover in headers)
{
HeaderList.Add(cover);
}
}));
});
}
}
}

@ -0,0 +1,32 @@
<Window x:Class="DatabaseManager.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/" xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
prism:ViewModelLocator.AutoWireViewModel="True"
Title="{Binding Title}" Height="700" Width="1200">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0" BorderThickness="0 0 1 0">
<ListBoxItem Content="Cover">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction Command="{Binding CoverCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBoxItem>
<ListBoxItem Content="Header">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction Command="{Binding HeaderCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBoxItem>
</ListBox>
<ContentControl Grid.Column="1" prism:RegionManager.RegionName="ContentRegion" />
</Grid>
</Window>

@ -0,0 +1,15 @@
using System.Windows;
namespace DatabaseManager.Views
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}

@ -0,0 +1,18 @@
<UserControl x:Class="DatabaseManager.Views.ViewCover"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<DataGrid ItemsSource="{Binding CoverList}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Avid" Binding="{Binding Avid}"/>
<DataGridTextColumn Header="Bvid" Binding="{Binding Bvid}"/>
<DataGridTextColumn Header="Cid" Binding="{Binding Cid}"/>
<DataGridTextColumn Header="Url" Binding="{Binding Url}"/>
<DataGridTextColumn Header="Md5" Binding="{Binding Md5}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>

@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace DatabaseManager.Views
{
/// <summary>
/// Interaction logic for ViewCover
/// </summary>
public partial class ViewCover : UserControl
{
public ViewCover()
{
InitializeComponent();
}
}
}

@ -0,0 +1,17 @@
<UserControl x:Class="DatabaseManager.Views.ViewHeader"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<DataGrid ItemsSource="{Binding HeaderList}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Avid" Binding="{Binding Mid}"/>
<DataGridTextColumn Header="Bvid" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Url" Binding="{Binding Url}"/>
<DataGridTextColumn Header="Md5" Binding="{Binding Md5}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>

@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace DatabaseManager.Views
{
/// <summary>
/// Interaction logic for ViewHeader
/// </summary>
public partial class ViewHeader : UserControl
{
public ViewHeader()
{
InitializeComponent();
}
}
}

@ -0,0 +1,35 @@
using DownKyi.Core.BiliApi.Video;
using DownKyi.Core.BiliApi.Video.Models;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
namespace DownKyi.Core.Test.BiliApi.Video
{
[TestClass]
public class DynamicTest
{
// 主分区tid
private readonly int[] typeIdList = { 1, 13, 167, 3, 129, 4, 36, 234, 223, 160, 211, 217, 119, 155, 5, 181, 177, 23, 11 };
[TestMethod]
public void TestRegionDynamicList()
{
foreach (var tid in typeIdList)
{
var time = DateTime.Now;
Random random = new Random(time.GetHashCode());
int ps = random.Next(1, 51);
var regionDynamicList = Dynamic.RegionDynamicList(tid, 1, ps);
Assert.IsInstanceOfType(regionDynamicList, typeof(List<DynamicVideoView>));
Assert.IsInstanceOfType(regionDynamicList[0], typeof(DynamicVideoView));
Assert.IsNotNull(regionDynamicList[0]);
Assert.AreEqual(regionDynamicList.Count, ps);
}
}
}
}

@ -0,0 +1,34 @@
using DownKyi.Core.BiliApi.Video;
using DownKyi.Core.BiliApi.Video.Models;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
namespace DownKyi.Core.Test.BiliApi.Video
{
[TestClass]
public class RankingTest
{
// 主分区tid
private readonly int[] typeIdList = { 1, 13, 167, 3, 129, 4, 36, 234, 223, 160, 211, 217, 119, 155, 5, 181, 177, 23, 11 };
[TestMethod]
public void TestRegionRankingList()
{
foreach (var tid in typeIdList)
{
var regionRankingList = Ranking.RegionRankingList(tid, 3);
Assert.IsInstanceOfType(regionRankingList, typeof(List<RankingVideoView>));
Assert.IsInstanceOfType(regionRankingList[0], typeof(RankingVideoView));
Assert.IsNotNull(regionRankingList[0]);
regionRankingList = Ranking.RegionRankingList(tid, 7);
Assert.IsInstanceOfType(regionRankingList, typeof(List<RankingVideoView>));
Assert.IsInstanceOfType(regionRankingList[0], typeof(RankingVideoView));
Assert.IsNotNull(regionRankingList[0]);
}
}
}
}

@ -0,0 +1,42 @@
using DownKyi.Core.BiliApi.Video;
using DownKyi.Core.BiliApi.Video.Models;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace DownKyi.Core.Test.BiliApi.Video
{
[TestClass]
public class VideoInfoTest
{
// 主分区tid
private readonly int[] typeIdList = { 1, 13, 167, 3, 129, 4, 36, 234, 223, 160, 211, 217, 119, 155, 5, 181, 177, 23, 11 };
[TestMethod]
public void TestVideoViewInfo()
{
foreach (var tid in typeIdList)
{
var regionDynamicList = Dynamic.RegionDynamicList(tid, 1, 12);
Assert.IsNotNull(regionDynamicList);
foreach (var videoView in regionDynamicList)
{
long aid = videoView.Aid;
string bvid = videoView.Bvid;
var bv = VideoInfo.VideoViewInfo(bvid, -1);
Assert.IsNotNull(bv);
Assert.IsInstanceOfType(bv, typeof(VideoView));
Assert.IsNotNull(bv.Pages);
var av = VideoInfo.VideoViewInfo(null, aid);
Assert.IsNotNull(av);
Assert.IsInstanceOfType(av, typeof(VideoView));
Assert.IsNotNull(av.Pages);
}
}
}
}
}

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{81B9719E-DA29-444F-BB72-CBD5819A654D}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DownKyi.Core.Test</RootNamespace>
<AssemblyName>DownKyi.Core.Test</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.2.1.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.2.1.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="BiliApi\Video\DynamicTest.cs" />
<Compile Include="BiliApi\Video\RankingTest.cs" />
<Compile Include="BiliApi\Video\VideoInfoTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Test\UnitTest1.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DownKyi.Core\DownKyi.Core.csproj">
<Project>{4fde0364-f65b-4812-bfe8-34e886624fbd}</Project>
<Name>DownKyi.Core</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.targets'))" />
</Target>
<Import Project="..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.targets')" />
</Project>

@ -0,0 +1,20 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("DownKyi.Core.Test")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DownKyi.Core.Test")]
[assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("81b9719e-da29-444f-bb72-cbd5819a654d")]
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

@ -0,0 +1,35 @@
using DownKyi.Core.BiliApi.Danmaku;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Net;
namespace DownKyi.Core.Test.Test
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
string url = "https://api.bilibili.com/x/v2/dm/web/seg.so?type=1&oid=421769791&pid=420986799&segment_index=1";
string localFile = @"C:\Users\FlySelf\Desktop\test.proto";
WebClient mywebclient = new WebClient();
mywebclient.DownloadFile(url, localFile);
}
[TestMethod]
public void TestMethod2()
{
var danmakus = DanmakuProtobuf.GetAllDanmakuProto(420986799, 421769791);
Console.WriteLine(danmakus.Count);
foreach (var dan in danmakus)
{
Console.WriteLine(dan.ToString());
}
}
}
}

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MSTest.TestAdapter" version="2.1.1" targetFramework="net472" />
<package id="MSTest.TestFramework" version="2.1.1" targetFramework="net472" />
</packages>

@ -0,0 +1,120 @@
using DownKyi.Core.Aria2cNet.Client;
using DownKyi.Core.Logging;
using System.Threading;
namespace DownKyi.Core.Aria2cNet
{
public class AriaManager
{
// gid对应项目的状态
public delegate void TellStatusHandler(long totalLength, long completedLength, long speed, string gid);
public event TellStatusHandler TellStatus;
protected virtual void OnTellStatus(long totalLength, long completedLength, long speed, string gid)
{
TellStatus?.Invoke(totalLength, completedLength, speed, gid);
}
// 下载结果回调
public delegate void DownloadFinishHandler(bool isSuccess, string downloadPath, string gid, string msg = null);
public event DownloadFinishHandler DownloadFinish;
protected virtual void OnDownloadFinish(bool isSuccess, string downloadPath, string gid, string msg = null)
{
DownloadFinish?.Invoke(isSuccess, downloadPath, gid, msg);
}
// 全局下载状态
public delegate void GetGlobalStatusHandler(long speed);
public event GetGlobalStatusHandler GlobalStatus;
protected virtual void OnGlobalStatus(long speed)
{
GlobalStatus?.Invoke(speed);
}
/// <summary>
/// 获取gid下载项的状态
/// </summary>
/// <param name="gid"></param>
/// <returns></returns>
public DownloadStatus GetDownloadStatus(string gid)
{
string filePath = "";
while (true)
{
var status = AriaClient.TellStatus(gid);
if (status == null || status.Result == null) { continue; }
if (status.Result.Result == null && status.Result.Error != null)
{
if (status.Result.Error.Message.Contains("is not found"))
{
OnDownloadFinish(false, null, gid, status.Result.Error.Message);
return DownloadStatus.ABORT;
}
}
if (status.Result.Result.Files != null && status.Result.Result.Files.Count >= 1)
{
filePath = status.Result.Result.Files[0].Path;
}
long totalLength = long.Parse(status.Result.Result.TotalLength);
long completedLength = long.Parse(status.Result.Result.CompletedLength);
long speed = long.Parse(status.Result.Result.DownloadSpeed);
// 回调
OnTellStatus(totalLength, completedLength, speed, gid);
if (status.Result.Result.Status == "complete")
{
break;
}
if (status.Result.Result.ErrorCode != null && status.Result.Result.ErrorCode != "0")
{
Utils.Debug.Console.PrintLine("ErrorMessage: " + status.Result.Result.ErrorMessage);
LogManager.Error("AriaManager", status.Result.Result.ErrorMessage);
//// 如果返回状态码不是200则继续
//if (status.Result.Result.ErrorMessage.Contains("The response status is not successful"))
//{
// Thread.Sleep(1000);
// continue;
//}
// aira中删除记录
var ariaRemove1 = AriaClient.RemoveDownloadResultAsync(gid);
Utils.Debug.Console.PrintLine(ariaRemove1);
LogManager.Debug("AriaManager", ariaRemove1.Result.Result);
// 返回回调信息,退出函数
OnDownloadFinish(false, null, gid, status.Result.Result.ErrorMessage);
return DownloadStatus.FAILED;
}
// 降低CPU占用
Thread.Sleep(100);
}
OnDownloadFinish(true, filePath, gid, null);
return DownloadStatus.SUCCESS;
}
/// <summary>
/// 获取全局下载速度
/// </summary>
public async void GetGlobalStatus()
{
while (true)
{
// 查询全局status
var globalStatus = await AriaClient.GetGlobalStatAsync();
if (globalStatus == null || globalStatus.Result == null) { continue; }
long globalSpeed = long.Parse(globalStatus.Result.DownloadSpeed);
// 回调
OnGlobalStatus(globalSpeed);
// 降低CPU占用
Thread.Sleep(100);
}
}
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,25 @@
using Newtonsoft.Json;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaAddMetalink
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public string Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,25 @@
using Newtonsoft.Json;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaAddTorrent
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public string Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,30 @@
using Newtonsoft.Json;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
//{
//"id": "downkyi",
//"jsonrpc": "2.0",
//"result": "1aac102a4875c8cd"
//}
[JsonObject]
public class AriaAddUri
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public string Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,25 @@
using Newtonsoft.Json;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaChangeOption
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public string Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,25 @@
using Newtonsoft.Json;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaChangePosition
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public int Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,26 @@
using Newtonsoft.Json;
using System.Collections.Generic;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaChangeUri
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public List<int> Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,23 @@
using Newtonsoft.Json;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
//"error": {
// "code": 1,
// "message": "Unauthorized"
//}
[JsonObject]
public class AriaError
{
[JsonProperty("code")]
public int Code { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,53 @@
using Newtonsoft.Json;
using System.Collections.Generic;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaGetFiles
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public List<AriaUri> Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
[JsonObject]
public class AriaGetFilesResult
{
[JsonProperty("completedLength")]
public string CompletedLength { get; set; }
[JsonProperty("index")]
public string Index { get; set; }
[JsonProperty("length")]
public string Length { get; set; }
[JsonProperty("path")]
public string Path { get; set; }
[JsonProperty("selected")]
public string Selected { get; set; }
[JsonProperty("uris")]
public List<AriaUri> Uris { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,66 @@
using Newtonsoft.Json;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
/*
{
"id": "qwer",
"jsonrpc": "2.0",
"result": {
"downloadSpeed": "0",
"numActive": "0",
"numStopped": "0",
"numStoppedTotal": "0",
"numWaiting": "0",
"uploadSpeed": "0"
}
}
*/
[JsonObject]
public class AriaGetGlobalStat
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public AriaGetGlobalStatResult Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
[JsonObject]
public class AriaGetGlobalStatResult
{
[JsonProperty("downloadSpeed")]
public string DownloadSpeed { get; set; }
[JsonProperty("numActive")]
public string NumActive { get; set; }
[JsonProperty("numStopped")]
public string NumStopped { get; set; }
[JsonProperty("numStoppedTotal")]
public string NumStoppedTotal { get; set; }
[JsonProperty("numWaiting")]
public string NumWaiting { get; set; }
[JsonProperty("uploadSpeed")]
public string UploadSpeed { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,25 @@
using Newtonsoft.Json;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaGetOption
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public AriaOption Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,62 @@
using Newtonsoft.Json;
using System.Collections.Generic;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaGetPeers
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public List<AriaPeer> Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
[JsonObject]
public class AriaPeer
{
[JsonProperty("amChoking")]
public string AmChoking { get; set; }
[JsonProperty("bitfield")]
public string Bitfield { get; set; }
[JsonProperty("downloadSpeed")]
public string DownloadSpeed { get; set; }
[JsonProperty("ip")]
public string Ip { get; set; }
[JsonProperty("peerChoking")]
public string PeerChoking { get; set; }
[JsonProperty("peerId")]
public string PeerId { get; set; }
[JsonProperty("port")]
public string Port { get; set; }
[JsonProperty("seeder")]
public string Seeder { get; set; }
[JsonProperty("uploadSpeed")]
public string UploadSpeed { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,59 @@
using Newtonsoft.Json;
using System.Collections.Generic;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaGetServers
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public List<AriaGetServersResult> Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
[JsonObject]
public class AriaGetServersResult
{
[JsonProperty("index")]
public string Index { get; set; }
[JsonProperty("servers")]
public List<AriaResultServer> Servers { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
[JsonObject]
public class AriaResultServer
{
[JsonProperty("currentUri")]
public string CurrentUri { get; set; }
[JsonProperty("downloadSpeed")]
public string DownloadSpeed { get; set; }
[JsonProperty("uri")]
public string Uri { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,37 @@
using Newtonsoft.Json;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaGetSessionInfo
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public AriaGetSessionInfoResult Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
[JsonObject]
public class AriaGetSessionInfoResult
{
[JsonProperty("sessionId")]
public string SessionId { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,26 @@
using Newtonsoft.Json;
using System.Collections.Generic;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaGetUris
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public List<AriaUri> Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,244 @@
using Newtonsoft.Json;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaOption
{
[JsonProperty("all-proxy")]
public string AllProxy { get; set; }
[JsonProperty("allow-overwrite")]
public string AllowOverwrite { get; set; }
[JsonProperty("allow-piece-length-change")]
public string AllowPieceLengthChange { get; set; }
[JsonProperty("always-resume")]
public string AlwaysResume { get; set; }
[JsonProperty("async-dns")]
public string AsyncDns { get; set; }
[JsonProperty("auto-file-renaming")]
public string AutoFileRenaming { get; set; }
[JsonProperty("bt-enable-hook-after-hash-check")]
public string BtEnableHookAfterHashCheck { get; set; }
[JsonProperty("bt-enable-lpd")]
public string BtEnableLpd { get; set; }
[JsonProperty("bt-force-encryption")]
public string BtForceEncryption { get; set; }
[JsonProperty("bt-hash-check-seed")]
public string BtHashCheckSeed { get; set; }
[JsonProperty("bt-load-saved-metadata")]
public string BtLoadSavedMetadata { get; set; }
[JsonProperty("bt-max-peers")]
public string BtMaxPeers { get; set; }
[JsonProperty("bt-metadata-only")]
public string BtMetadataOnly { get; set; }
[JsonProperty("bt-min-crypto-level")]
public string BtMinCryptoLevel { get; set; }
[JsonProperty("bt-remove-unselected-file")]
public string BtRemoveUnselectedFile { get; set; }
[JsonProperty("bt-request-peer-speed-limit")]
public string BtRequestPeerSpeedLimit { get; set; }
[JsonProperty("bt-require-crypto")]
public string BtRequireCrypto { get; set; }
[JsonProperty("bt-save-metadata")]
public string BtSaveMetadata { get; set; }
[JsonProperty("bt-seed-unverified")]
public string BtSeedUnverified { get; set; }
[JsonProperty("bt-stop-timeout")]
public string BtStopTimeout { get; set; }
[JsonProperty("bt-tracker-connect-timeout")]
public string BtTrackerConnectTimeout { get; set; }
[JsonProperty("bt-tracker-interval")]
public string BtTrackerInterval { get; set; }
[JsonProperty("bt-tracker-timeout")]
public string BtTrackerTimeout { get; set; }
[JsonProperty("check-integrity")]
public string CheckIntegrity { get; set; }
[JsonProperty("conditional-get")]
public string ConditionalGet { get; set; }
[JsonProperty("connect-timeout")]
public string ConnectTimeout { get; set; }
[JsonProperty("content-disposition-default-utf8")]
public string ContentDispositionDefaultUtf8 { get; set; }
[JsonProperty("continue")]
public string Continue { get; set; }
[JsonProperty("dir")]
public string Dir { get; set; }
[JsonProperty("dry-run")]
public string DryRun { get; set; }
[JsonProperty("enable-http-keep-alive")]
public string EnableHttpKeepAlive { get; set; }
[JsonProperty("enable-http-pipelining")]
public string EnableHttpPipelining { get; set; }
[JsonProperty("enable-mmap")]
public string EnableMmap { get; set; }
[JsonProperty("enable-peer-exchange")]
public string EnablePeerExchange { get; set; }
[JsonProperty("file-allocation")]
public string FileAllocation { get; set; }
[JsonProperty("follow-metalink")]
public string FollowMetalink { get; set; }
[JsonProperty("follow-torrent")]
public string FollowTorrent { get; set; }
[JsonProperty("force-save")]
public string ForceSave { get; set; }
[JsonProperty("ftp-pasv")]
public string FtpPasv { get; set; }
[JsonProperty("ftp-reuse-connection")]
public string FtpReuseConnection { get; set; }
[JsonProperty("ftp-type")]
public string FtpType { get; set; }
[JsonProperty("hash-check-only")]
public string HashCheckOnly { get; set; }
[JsonProperty("http-accept-gzip")]
public string HttpAcceptGzip { get; set; }
[JsonProperty("http-auth-challenge")]
public string HttpAuthChallenge { get; set; }
[JsonProperty("http-no-cache")]
public string HttpNoCache { get; set; }
[JsonProperty("lowest-speed-limit")]
public string LowestSpeedLimit { get; set; }
[JsonProperty("max-connection-per-server")]
public string MaxConnectionPerServer { get; set; }
[JsonProperty("max-download-limit")]
public string MaxDownloadLimit { get; set; }
[JsonProperty("max-file-not-found")]
public string MaxFileNotFound { get; set; }
[JsonProperty("max-mmap-limit")]
public string MaxMmapLimit { get; set; }
[JsonProperty("max-resume-failure-tries")]
public string MaxResumeFailureTries { get; set; }
[JsonProperty("max-tries")]
public string MaxTries { get; set; }
[JsonProperty("max-upload-limit")]
public string MaxUploadLimit { get; set; }
[JsonProperty("metalink-enable-unique-protocol")]
public string MetalinkEnableUniqueProtocol { get; set; }
[JsonProperty("metalink-preferred-protocol")]
public string MetalinkPreferredProtocol { get; set; }
[JsonProperty("min-split-size")]
public string MinSplitSize { get; set; }
[JsonProperty("no-file-allocation-limit")]
public string NoFileAllocationLimit { get; set; }
[JsonProperty("no-netrc")]
public string NoNetrc { get; set; }
[JsonProperty("out")]
public string Out { get; set; }
[JsonProperty("parameterized-uri")]
public string ParameterizedUri { get; set; }
[JsonProperty("pause-metadata")]
public string PauseMetadata { get; set; }
[JsonProperty("piece-length")]
public string PieceLength { get; set; }
[JsonProperty("proxy-method")]
public string ProxyMethod { get; set; }
[JsonProperty("realtime-chunk-checksum")]
public string RealtimeChunkChecksum { get; set; }
[JsonProperty("remote-time")]
public string RemoteTime { get; set; }
[JsonProperty("remove-control-file")]
public string RemoveControlFile { get; set; }
[JsonProperty("retry-wait")]
public string RetryWait { get; set; }
[JsonProperty("reuse-uri")]
public string ReuseUri { get; set; }
[JsonProperty("rpc-save-upload-metadata")]
public string RpcSaveUploadMetadata { get; set; }
[JsonProperty("save-not-found")]
public string SaveNotFound { get; set; }
[JsonProperty("seed-ratio")]
public string SeedRatio { get; set; }
[JsonProperty("split")]
public string Split { get; set; }
[JsonProperty("stream-piece-selector")]
public string StreamPieceSelector { get; set; }
[JsonProperty("timeout")]
public string Timeout { get; set; }
[JsonProperty("uri-selector")]
public string UriSelector { get; set; }
[JsonProperty("use-head")]
public string UseHead { get; set; }
[JsonProperty("user-agent")]
public string UserAgent { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,25 @@
using Newtonsoft.Json;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaPause
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public string Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,25 @@
using Newtonsoft.Json;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaRemove
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public string Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,25 @@
using Newtonsoft.Json;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaSaveSession
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public string Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,53 @@
using Newtonsoft.Json;
using System.Collections.Generic;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaSendData
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("method")]
public string Method { get; set; }
[JsonProperty("params")]
public List<object> Params { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
[JsonObject]
public class AriaSendOption
{
[JsonProperty("all-proxy")]
public string HttpProxy { get; set; }
[JsonProperty("out")]
public string Out { get; set; }
[JsonProperty("dir")]
public string Dir { get; set; }
//[JsonProperty("header")]
//public string Header { get; set; }
//[JsonProperty("use-head")]
//public string UseHead { get; set; }
//[JsonProperty("user-agent")]
//public string UserAgent { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,25 @@
using Newtonsoft.Json;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaShutdown
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public string Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,128 @@
using Newtonsoft.Json;
using System.Collections.Generic;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaTellStatus
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public AriaTellStatusResult Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
[JsonObject]
public class AriaTellStatusList
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public List<AriaTellStatusResult> Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
[JsonObject]
public class AriaTellStatusResult
{
[JsonProperty("bitfield")]
public string Bitfield { get; set; }
[JsonProperty("completedLength")]
public string CompletedLength { get; set; }
[JsonProperty("connections")]
public string Connections { get; set; }
[JsonProperty("dir")]
public string Dir { get; set; }
[JsonProperty("downloadSpeed")]
public string DownloadSpeed { get; set; }
[JsonProperty("errorCode")]
public string ErrorCode { get; set; }
[JsonProperty("errorMessage")]
public string ErrorMessage { get; set; }
[JsonProperty("files")]
public List<AriaTellStatusResultFile> Files { get; set; }
[JsonProperty("gid")]
public string Gid { get; set; }
[JsonProperty("numPieces")]
public string NumPieces { get; set; }
[JsonProperty("pieceLength")]
public string PieceLength { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("totalLength")]
public string TotalLength { get; set; }
[JsonProperty("uploadLength")]
public string UploadLength { get; set; }
[JsonProperty("uploadSpeed")]
public string UploadSpeed { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
[JsonObject]
public class AriaTellStatusResultFile
{
[JsonProperty("completedLength")]
public string CompletedLength { get; set; }
[JsonProperty("index")]
public string Index { get; set; }
[JsonProperty("length")]
public string Length { get; set; }
[JsonProperty("path")]
public string Path { get; set; }
[JsonProperty("selected")]
public string Selected { get; set; }
[JsonProperty("uris")]
public List<AriaUri> Uris { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,19 @@
using Newtonsoft.Json;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaUri
{
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("uri")]
public string Uri { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,41 @@
using Newtonsoft.Json;
using System.Collections.Generic;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class AriaVersion
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public AriaVersionResult Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
[JsonObject]
public class AriaVersionResult
{
[JsonProperty("enabledFeatures")]
public List<string> EnabledFeatures { get; set; }
[JsonProperty("version")]
public string Version { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,26 @@
using Newtonsoft.Json;
using System.Collections.Generic;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class SystemListMethods
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public List<string> Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,26 @@
using Newtonsoft.Json;
using System.Collections.Generic;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class SystemListNotifications
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public List<string> Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,25 @@
using Newtonsoft.Json;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class SystemMulticall
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("jsonrpc")]
public string Jsonrpc { get; set; }
[JsonProperty("result")]
public string Result { get; set; }
[JsonProperty("error")]
public AriaError Error { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}

@ -0,0 +1,15 @@
using Newtonsoft.Json;
using System.Collections.Generic;
namespace DownKyi.Core.Aria2cNet.Client.Entity
{
[JsonObject]
public class SystemMulticallMathod
{
[JsonProperty("method")]
public string Method { get; set; }
[JsonProperty("params")]
public List<object> Params { get; set; }
}
}

@ -0,0 +1,12 @@
namespace DownKyi.Core.Aria2cNet.Client
{
/// <summary>
/// changePosition函数的how参数
/// </summary>
public enum HowChangePosition
{
POS_SET = 1,
POS_CUR,
POS_END
}
}

@ -0,0 +1,12 @@
namespace DownKyi.Core.Aria2cNet
{
/// <summary>
/// 下载状态
/// </summary>
public enum DownloadStatus
{
SUCCESS = 1,
FAILED,
ABORT
}
}

@ -0,0 +1,27 @@
using System.Collections.Generic;
namespace DownKyi.Core.Aria2cNet.Server
{
/// <summary>
/// Aria服务器的启动配置
/// </summary>
public class AriaConfig
{
public int ListenPort { get; set; } // 服务器端口号取值1024-65535
public string Token { get; set; } // 连接服务器的token
public AriaConfigLogLevel LogLevel { get; set; } // 日志等级debug info notice warn error
public int MaxConcurrentDownloads { get; set; } // 最大同时下载数(任务数)取值1-*
public int MaxConnectionPerServer { get; set; } // 同服务器连接数取值1-16
public int Split { get; set; } // 单文件最大线程数取值1-*
//public int MaxTries { get; set; } //当服务器返回503错误时尝试重连尝试重连次数0代表无限默认:5
public int MinSplitSize { get; set; } // 最小文件分片大小, 下载线程数上限取决于能分出多少片, 对于小文件重要单位MB
public long MaxOverallDownloadLimit { get; set; } // 下载速度限制取值1-*
public long MaxDownloadLimit { get; set; } // 下载单文件速度限制取值1-*
public long MaxOverallUploadLimit { get; set; } // 上传速度限制取值1-*
public long MaxUploadLimit { get; set; } // 上传单文件速度限制取值1-*
public bool ContinueDownload { get; set; } // 断点续传
public AriaConfigFileAllocation FileAllocation { get; set; } // 文件预分配, none prealloc
public List<string> Headers { get; set; }
}
}

@ -0,0 +1,12 @@
namespace DownKyi.Core.Aria2cNet.Server
{
/// <summary>
/// 文件预分配
/// </summary>
public enum AriaConfigFileAllocation
{
NONE = 1, // 没有预分配
PREALLOC, // 预分配,默认
FALLOC // NTFS建议使用
}
}

@ -0,0 +1,14 @@
namespace DownKyi.Core.Aria2cNet.Server
{
/// <summary>
/// 日志等级
/// </summary>
public enum AriaConfigLogLevel
{
DEBUG = 1,
INFO,
NOTICE,
WARN,
ERROR
}
}

@ -0,0 +1,245 @@
using DownKyi.Core.Aria2cNet.Client;
using DownKyi.Core.Logging;
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace DownKyi.Core.Aria2cNet.Server
{
public static class AriaServer
{
public static int ListenPort; // 服务器端口
private static Process Server;
/// <summary>
/// 启动aria2c服务器
/// </summary>
/// <param name="listenPort"></param>
/// <param name="output"></param>
/// <param name="window"></param>
/// <returns></returns>
public static async Task<bool> StartServerAsync(AriaConfig config, TextBox output = null, Window window = null)
{
// aria端口
ListenPort = config.ListenPort;
// aria目录
string ariaDir = Environment.CurrentDirectory + "\\aria\\";
// 会话文件
#if DEBUG
string sessionFile = ariaDir + "aira.session";
#else
string sessionFile = ariaDir + "aira.session.gz";
#endif
// 日志文件
string logFile = ariaDir + "aira.log";
// 自动保存会话文件的时间间隔
int saveSessionInterval = 30;
// --enable-rpc --rpc-listen-all=true --rpc-allow-origin-all=true --continue=true
await Task.Run(() =>
{
// 创建目录和文件
if (!Directory.Exists(ariaDir))
{
Directory.CreateDirectory(ariaDir);
}
if (!File.Exists(sessionFile))
{
var stream = File.Create(sessionFile);
stream.Close();
}
if (!File.Exists(logFile))
{
var stream = File.Create(logFile);
stream.Close();
}
else
{
// 日志文件存在如果大于100M则删除
try
{
var stream = File.Open(logFile, FileMode.Open);
if (stream.Length >= 512 * 1024 * 1024L)
{
stream.SetLength(0);
}
stream.Close();
}
catch (Exception e)
{
Utils.Debug.Console.PrintLine("StartServerAsync()发生其他异常: {0}", e);
LogManager.Error("AriaServer", e);
}
}
// header 解析
string headers = string.Empty;
if (config.Headers != null)
{
foreach (var header in config.Headers)
{
headers += $"--header=\"{header}\" ";
}
}
ExcuteProcess("aria2c.exe",
$"--enable-rpc --rpc-listen-all=true --rpc-allow-origin-all=true " +
$"--rpc-listen-port={config.ListenPort} " +
$"--rpc-secret={config.Token} " +
$"--input-file=\"{sessionFile}\" --save-session=\"{sessionFile}\" " +
$"--save-session-interval={saveSessionInterval} " +
$"--log=\"{logFile}\" --log-level={config.LogLevel.ToString("G").ToLower()} " + // log-level: 'debug' 'info' 'notice' 'warn' 'error'
$"--max-concurrent-downloads={config.MaxConcurrentDownloads} " + // 最大同时下载数(任务数)
$"--max-connection-per-server={config.MaxConnectionPerServer} " + // 同服务器连接数
$"--split={config.Split} " + // 单文件最大线程数
//$"--max-tries={config.MaxTries} retry-wait=3 " + // 尝试重连次数
$"--min-split-size={config.MinSplitSize}M " + // 最小文件分片大小, 下载线程数上限取决于能分出多少片, 对于小文件重要
$"--max-overall-download-limit={config.MaxOverallDownloadLimit} " + // 下载速度限制
$"--max-download-limit={config.MaxDownloadLimit} " + // 下载单文件速度限制
$"--max-overall-upload-limit={config.MaxOverallUploadLimit} " + // 上传速度限制
$"--max-upload-limit={config.MaxUploadLimit} " + // 上传单文件速度限制
$"--continue={config.ContinueDownload.ToString().ToLower()} " + // 断点续传
$"--allow-overwrite=true " + // 允许复写文件
$"--auto-file-renaming=false " +
$"--file-allocation={config.FileAllocation.ToString("G").ToLower()} " + // 文件预分配, none prealloc
$"{headers}" + // header
"",
null, (s, e) =>
{
if (e.Data == null || e.Data == "" || e.Data.Replace(" ", "") == "") { return; }
Utils.Debug.Console.PrintLine(e.Data);
LogManager.Debug("AriaServer", e.Data);
if (output != null && window != null)
{
window.Dispatcher.Invoke(new Action(() =>
{
output.Text += e.Data + "\n";
output.ScrollToEnd();
}));
}
});
});
return true;
}
/// <summary>
/// 关闭aria2c服务器异步方法
/// </summary>
/// <returns></returns>
public static async Task<bool> CloseServerAsync()
{
await AriaClient.ShutdownAsync();
return true;
}
/// <summary>
/// 强制关闭aria2c服务器异步方法
/// </summary>
/// <returns></returns>
public static async Task<bool> ForceCloseServerAsync()
{
//await Task.Run(() =>
//{
// if (Server == null) { return; }
// Server.Kill();
// Server = null; // 将Server指向null
//});
//return true;
await AriaClient.ForceShutdownAsync();
return true;
}
/// <summary>
/// 关闭aria2c服务器
/// </summary>
/// <returns></returns>
public static bool CloseServer()
{
var task = AriaClient.ShutdownAsync();
if (task.Result != null && task.Result.Result != null && task.Result.Result == "OK")
{ return true; }
return false;
}
/// <summary>
/// 强制关闭aria2c服务器
/// </summary>
/// <returns></returns>
public static bool ForceCloseServer()
{
var task = AriaClient.ForceShutdownAsync();
if (task.Result != null && task.Result.Result != null && task.Result.Result == "OK")
{ return true; }
return false;
}
/// <summary>
/// 杀死Aria进程
/// </summary>
/// <param name="processName"></param>
/// <returns></returns>
public static bool KillServer(string processName = "aria2c")
{
Process[] processes = Process.GetProcessesByName(processName);
foreach (var process in processes)
{
try
{
process.Kill();
}
catch (Exception e)
{
Utils.Debug.Console.PrintLine("KillServer()发生异常: {0}", e);
LogManager.Error("AriaServer", e);
}
}
return true;
}
private static void ExcuteProcess(string exe, string arg, string workingDirectory, DataReceivedEventHandler output)
{
using (var p = new Process())
{
Server = p;
p.StartInfo.FileName = exe;
p.StartInfo.Arguments = arg;
// 工作目录
if (workingDirectory != null)
{
p.StartInfo.WorkingDirectory = workingDirectory;
}
// 输出信息重定向
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
// 将 StandardErrorEncoding 改为 UTF-8 才不会出现中文乱码
p.StartInfo.StandardOutputEncoding = Encoding.UTF8;
p.StartInfo.StandardErrorEncoding = Encoding.UTF8;
p.OutputDataReceived += output;
p.ErrorDataReceived += output;
// 启动线程
p.Start();
p.BeginOutputReadLine();
p.BeginErrorReadLine();
// 等待进程结束
p.WaitForExit();
}
}
}
}

@ -0,0 +1,68 @@
using DownKyi.Core.BiliApi.Bangumi.Models;
using DownKyi.Core.Logging;
using Newtonsoft.Json;
using System;
namespace DownKyi.Core.BiliApi.Bangumi
{
public static class BangumiInfo
{
/// <summary>
/// 剧集基本信息mediaId方式
/// </summary>
/// <param name="mediaId"></param>
/// <returns></returns>
public static BangumiMedia BangumiMediaInfo(long mediaId)
{
string url = $"https://api.bilibili.com/pgc/review/user?media_id={mediaId}";
string referer = "https://www.bilibili.com";
string response = WebClient.RequestWeb(url, referer);
try
{
var media = JsonConvert.DeserializeObject<BangumiMediaOrigin>(response);
if (media != null && media.Result != null) { return media.Result.Media; }
else { return null; }
}
catch (Exception e)
{
Utils.Debug.Console.PrintLine("BangumiMediaInfo()发生异常: {0}", e);
LogManager.Error("BangumiInfo", e);
return null;
}
}
/// <summary>
/// 获取剧集明细web端seasonId/episodeId方式
/// </summary>
/// <param name="seasonId"></param>
/// <param name="episodeId"></param>
/// <returns></returns>
public static BangumiSeason BangumiSeasonInfo(long seasonId = -1, long episodeId = -1)
{
string baseUrl = "https://api.bilibili.com/pgc/view/web/season";
string referer = "https://www.bilibili.com";
string url;
if (seasonId > -1) { url = $"{baseUrl}?season_id={seasonId}"; }
else if (episodeId > -1) { url = $"{baseUrl}?ep_id={episodeId}"; }
else { return null; }
string response = WebClient.RequestWeb(url, referer);
try
{
var bangumiSeason = JsonConvert.DeserializeObject<BangumiSeasonOrigin>(response);
if (bangumiSeason != null) { return bangumiSeason.Result; }
else { return null; }
}
catch (Exception e)
{
Utils.Debug.Console.PrintLine("BangumiSeasonInfo()发生异常: {0}", e);
LogManager.Error("BangumiInfo", e);
return null;
}
}
}
}

@ -0,0 +1,22 @@
using System.Collections.Generic;
namespace DownKyi.Core.BiliApi.Bangumi
{
public static class BangumiType
{
public static Dictionary<int, string> Type = new Dictionary<int, string>()
{
{ 1, "Anime" },
{ 2, "Movie" },
{ 3, "Documentary" },
{ 4, "Guochuang" },
{ 5, "TV" },
{ 6, "Unknown" },
{ 7, "Entertainment" },
{ 8, "Unknown" },
{ 9, "Unknown" },
{ 10, "Unknown" }
};
}
}

@ -0,0 +1,13 @@
using DownKyi.Core.BiliApi.Models;
using Newtonsoft.Json;
namespace DownKyi.Core.BiliApi.Bangumi.Models
{
public class BangumiArea : BaseModel
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
}

@ -0,0 +1,53 @@
using DownKyi.Core.BiliApi.Models;
using Newtonsoft.Json;
namespace DownKyi.Core.BiliApi.Bangumi.Models
{
public class BangumiEpisode : BaseModel
{
[JsonProperty("aid")]
public long Aid { get; set; }
[JsonProperty("badge")]
public string Badge { get; set; }
// badge_info
// badge_type
[JsonProperty("bvid")]
public string Bvid { get; set; }
[JsonProperty("cid")]
public long Cid { get; set; }
[JsonProperty("cover")]
public string Cover { get; set; }
[JsonProperty("dimension")]
public Dimension Dimension { get; set; }
[JsonProperty("duration")]
public long Duration { get; set; }
[JsonProperty("from")]
public string From { get; set; }
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("link")]
public string Link { get; set; }
[JsonProperty("long_title")]
public string LongTitle { get; set; }
[JsonProperty("pub_time")]
public long PubTime { get; set; }
// pv
// release_date
// rights
[JsonProperty("share_copy")]
public string ShareCopy { get; set; }
[JsonProperty("share_url")]
public string ShareUrl { get; set; }
[JsonProperty("short_link")]
public string ShortLink { get; set; }
// stat
[JsonProperty("status")]
public int Status { get; set; }
[JsonProperty("subtitle")]
public string Subtitle { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("vid")]
public string Vid { get; set; }
}
}

@ -0,0 +1,46 @@
using DownKyi.Core.BiliApi.Models;
using Newtonsoft.Json;
using System.Collections.Generic;
namespace DownKyi.Core.BiliApi.Bangumi.Models
{
// https://api.bilibili.com/pgc/review/user
public class BangumiMediaOrigin : BaseModel
{
//[JsonProperty("code")]
//public int Code { get; set; }
//[JsonProperty("message")]
//public string Message { get; set; }
//[JsonProperty("ttl")]
//public int Ttl { get; set; }
[JsonProperty("result")]
public BangumiMediaData Result { get; set; }
}
public class BangumiMediaData : BaseModel
{
[JsonProperty("media")]
public BangumiMedia Media { get; set; }
}
public class BangumiMedia : BaseModel
{
[JsonProperty("areas")]
public List<BangumiArea> Areas { get; set; }
[JsonProperty("cover")]
public string Cover { get; set; }
[JsonProperty("media_id")]
public long MediaId { get; set; }
// new_ep
// rating
[JsonProperty("season_id")]
public long SeasonId { get; set; }
[JsonProperty("share_url")]
public string ShareUrl { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("type_name")]
public string TypeName { get; set; }
}
}

@ -0,0 +1,13 @@
using DownKyi.Core.BiliApi.Models;
using Newtonsoft.Json;
namespace DownKyi.Core.BiliApi.Bangumi.Models
{
public class BangumiPositive : BaseModel
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
}
}

@ -0,0 +1,79 @@
using DownKyi.Core.BiliApi.Models;
using Newtonsoft.Json;
using System.Collections.Generic;
namespace DownKyi.Core.BiliApi.Bangumi.Models
{
// https://api.bilibili.com/pgc/view/web/season
public class BangumiSeasonOrigin : BaseModel
{
//[JsonProperty("code")]
//public int Code { get; set; }
//[JsonProperty("message")]
//public string Message { get; set; }
//[JsonProperty("ttl")]
//public int Ttl { get; set; }
[JsonProperty("result")]
public BangumiSeason Result { get; set; }
}
public class BangumiSeason : BaseModel
{
// activity
// alias
[JsonProperty("areas")]
public List<BangumiArea> Areas { get; set; }
[JsonProperty("bkg_cover")]
public string BkgCover { get; set; }
[JsonProperty("cover")]
public string Cover { get; set; }
[JsonProperty("episodes")]
public List<BangumiEpisode> Episodes { get; set; }
[JsonProperty("evaluate")]
public string Evaluate { get; set; }
// freya
// jp_title
[JsonProperty("link")]
public string Link { get; set; }
[JsonProperty("media_id")]
public long MediaId { get; set; }
// mode
// new_ep
// payment
[JsonProperty("positive")]
public BangumiPositive Positive { get; set; }
// publish
// rating
// record
// rights
[JsonProperty("season_id")]
public long SeasonId { get; set; }
[JsonProperty("season_title")]
public string SeasonTitle { get; set; }
[JsonProperty("seasons")]
public List<BangumiSeasonInfo> Seasons { get; set; }
[JsonProperty("section")]
public List<BangumiSection> Section { get; set; }
// series
// share_copy
// share_sub_title
// share_url
// show
[JsonProperty("square_cover")]
public string SquareCover { get; set; }
[JsonProperty("stat")]
public BangumiStat Stat { get; set; }
// status
[JsonProperty("subtitle")]
public string Subtitle { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("total")]
public int Total { get; set; }
[JsonProperty("type")]
public int Type { get; set; }
[JsonProperty("up_info")]
public BangumiUpInfo UpInfo { get; set; }
}
}

@ -0,0 +1,25 @@
using DownKyi.Core.BiliApi.Models;
using Newtonsoft.Json;
namespace DownKyi.Core.BiliApi.Bangumi.Models
{
public class BangumiSeasonInfo : BaseModel
{
[JsonProperty("badge")]
public string Badge { get; set; }
// badge_info
// badge_type
[JsonProperty("cover")]
public string Cover { get; set; }
[JsonProperty("media_id")]
public long MediaId { get; set; }
// new_ep
[JsonProperty("season_id")]
public long SeasonId { get; set; }
[JsonProperty("season_title")]
public string SeasonTitle { get; set; }
[JsonProperty("season_type")]
public int SeasonType { get; set; }
// stat
}
}

@ -0,0 +1,20 @@
using DownKyi.Core.BiliApi.Models;
using Newtonsoft.Json;
using System.Collections.Generic;
namespace DownKyi.Core.BiliApi.Bangumi.Models
{
public class BangumiSection : BaseModel
{
[JsonProperty("episode_id")]
public long EpisodeId { get; set; }
[JsonProperty("episodes")]
public List<BangumiEpisode> Episodes { get; set; }
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("type")]
public int Type { get; set; }
}
}

@ -0,0 +1,25 @@
using DownKyi.Core.BiliApi.Models;
using Newtonsoft.Json;
namespace DownKyi.Core.BiliApi.Bangumi.Models
{
public class BangumiStat : BaseModel
{
[JsonProperty("coins")]
public long Coins { get; set; }
[JsonProperty("danmakus")]
public long Danmakus { get; set; }
[JsonProperty("favorite")]
public long Favorite { get; set; }
[JsonProperty("favorites")]
public long Favorites { get; set; }
[JsonProperty("likes")]
public long Likes { get; set; }
[JsonProperty("reply")]
public long Reply { get; set; }
[JsonProperty("share")]
public long Share { get; set; }
[JsonProperty("views")]
public long Views { get; set; }
}
}

@ -0,0 +1,22 @@
using DownKyi.Core.BiliApi.Models;
using Newtonsoft.Json;
namespace DownKyi.Core.BiliApi.Bangumi.Models
{
public class BangumiUpInfo : BaseModel
{
[JsonProperty("avatar")]
public string Avatar { get; set; }
// follower
// is_follow
[JsonProperty("mid")]
public long Mid { get; set; }
// pendant
// theme_type
[JsonProperty("uname")]
public string Name { get; set; }
// verify_type
// vip_status
// vip_type
}
}

@ -0,0 +1,63 @@
using System;
namespace DownKyi.Core.BiliApi.BiliUtils
{
public static class BvId
{
private const string tableStr = "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF"; //码表
private static readonly char[] table = tableStr.ToCharArray();
private static readonly char[] tr = new char[124]; //反查码表
private const ulong Xor = 177451812; //固定异或值
private const ulong add = 8728348608; //固定加法值
private static readonly int[] s = { 11, 10, 3, 8, 4, 6 }; //位置编码表
static BvId()
{
Tr_init();
}
//初始化反查码表
private static void Tr_init()
{
for (int i = 0; i < 58; i++)
tr[table[i]] = (char)i;
}
/// <summary>
/// bvid转avid
/// </summary>
/// <param name="bvid"></param>
/// <returns></returns>
public static ulong Bv2Av(string bvid)
{
char[] bv = bvid.ToCharArray();
ulong r = 0;
ulong av;
for (int i = 0; i < 6; i++)
r += tr[bv[s[i]]] * (ulong)Math.Pow(58, i);
av = (r - add) ^ Xor;
return av;
}
/// <summary>
/// avid转bvid
/// </summary>
/// <param name="av"></param>
/// <returns></returns>
public static string Av2Bv(ulong av)
{
//编码结果
string res = "BV1 4 1 7 ";
char[] result = res.ToCharArray();
av = (av ^ Xor) + add;
for (int i = 0; i < 6; i++)
result[s[i]] = table[av / (ulong)Math.Pow(58, i) % 58];
string bv = new string(result);
return bv;
}
}
}

@ -0,0 +1,18 @@
using System.Collections.Generic;
namespace DownKyi.Core.BiliApi.BiliUtils
{
public static class Constant
{
/// <summary>
/// 音质id及含义
/// </summary>
public static Dictionary<int, string> AudioQuality { get; } = new Dictionary<int, string>()
{
{ 30216, "64K" },
{ 30232, "132K" },
{ 30280, "192K" }
};
}
}

@ -0,0 +1,154 @@
using System;
namespace DownKyi.Core.BiliApi.BiliUtils
{
public static class DanmakuSender
{
private const uint CRCPOLYNOMIAL = 0xEDB88320;
private static readonly uint[] crctable = new uint[256];
static DanmakuSender()
{
CreateTable();
}
private static void CreateTable()
{
for (int i = 0; i < 256; i++)
{
uint crcreg = (uint)i;
for (int j = 0; j < 8; j++)
{
if ((crcreg & 1) != 0)
{
crcreg = CRCPOLYNOMIAL ^ (crcreg >> 1);
}
else
{
crcreg >>= 1;
}
}
crctable[i] = crcreg;
}
}
private static uint Crc32(string userId)
{
uint crcstart = 0xFFFFFFFF;
for (int i = 0; i < userId.Length; i++)
{
uint index = (uint)(crcstart ^ (int)userId[i]) & 255;
crcstart = (crcstart >> 8) ^ crctable[index];
}
return crcstart;
}
private static uint Crc32LastIndex(string userId)
{
uint index = 0;
uint crcstart = 0xFFFFFFFF;
for (int i = 0; i < userId.Length; i++)
{
index = (uint)((crcstart ^ (int)userId[i]) & 255);
crcstart = (crcstart >> 8) ^ crctable[index];
}
return index;
}
private static int GetCrcIndex(long t)
{
for (int i = 0; i < 256; i++)
{
if ((crctable[i] >> 24) == t)
{
return i;
}
}
return -1;
}
private static object[] DeepCheck(int i, int[] index)
{
object[] resultArray = new object[2];
string result = "";
uint tc;// = 0x00;
var hashcode = Crc32(i.ToString());
tc = (uint)(hashcode & 0xff ^ index[2]);
if (!(tc <= 57 && tc >= 48))
{
resultArray[0] = 0;
return resultArray;
}
result += (tc - 48).ToString();
hashcode = crctable[index[2]] ^ (hashcode >> 8);
tc = (uint)(hashcode & 0xff ^ index[1]);
if (!(tc <= 57 && tc >= 48))
{
resultArray[0] = 0;
return resultArray;
}
result += (tc - 48).ToString();
hashcode = crctable[index[1]] ^ (hashcode >> 8);
tc = (uint)(hashcode & 0xff ^ index[0]);
if (!(tc <= 57 && tc >= 48))
{
resultArray[0] = 0;
return resultArray;
}
result += (tc - 48).ToString();
//hashcode = crctable[index[0]] ^ (hashcode >> 8);
resultArray[0] = 1;
resultArray[1] = result;
return resultArray;
}
/// <summary>
/// 查询弹幕发送者
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public static string FindDanmakuSender(string userId)
{
object[] deepCheckData = new object[2];
int[] index = new int[4];
uint ht = (uint)Convert.ToInt32($"0x{userId}", 16);
ht ^= 0xffffffff;
int i;
for (i = 3; i > -1; i--)
{
index[3 - i] = GetCrcIndex(ht >> (i * 8));
uint snum = crctable[index[3 - i]];
ht ^= snum >> ((3 - i) * 8);
}
for (i = 0; i < 100000000; i++)
{
uint lastindex = Crc32LastIndex(i.ToString());
if (lastindex == index[3])
{
deepCheckData = DeepCheck(i, index);
if ((int)deepCheckData[0] != 0)
{
break;
}
}
}
if (i == 100000000)
{
return "-1";
}
return $"{i}{deepCheckData[1]}";
}
}
}

@ -0,0 +1,503 @@
using DownKyi.Core.Utils.Validator;
using System.Text.RegularExpressions;
namespace DownKyi.Core.BiliApi.BiliUtils
{
/// <summary>
/// 解析输入的字符串<para/>
/// 支持的格式有:<para/>
/// av号av170001, AV170001, https://www.bilibili.com/video/av170001 <para/>
/// BV号BV17x411w7KC, https://www.bilibili.com/video/BV17x411w7KC <para/>
/// 番剧电影、电视剧ss号ss32982, SS32982, https://www.bilibili.com/bangumi/play/ss32982 <para/>
/// 番剧电影、电视剧ep号ep317925, EP317925, https://www.bilibili.com/bangumi/play/ep317925 <para/>
/// 番剧电影、电视剧md号md28228367, MD28228367, https://www.bilibili.com/bangumi/media/md28228367 <para/>
/// 课程ss号https://www.bilibili.com/cheese/play/ss205 <para/>
/// 课程ep号https://www.bilibili.com/cheese/play/ep3489 <para/>
/// 收藏夹ml1329019876, ML1329019876, https://www.bilibili.com/medialist/detail/ml1329019876 <para/>
/// 用户空间uid928123, UID928123, uid:928123, UID:928123, https://space.bilibili.com/928123
/// </summary>
public static class ParseEntrance
{
public static readonly string WwwUrl = "https://www.bilibili.com";
public static readonly string MobileUrl = "https://m.bilibili.com";
public static readonly string SpaceUrl = "https://space.bilibili.com";
public static readonly string VideoUrl = $"{WwwUrl}/video/";
public static readonly string BangumiUrl = $"{WwwUrl}/bangumi/play/";
public static readonly string BangumiMediaUrl = $"{WwwUrl}/bangumi/media/";
public static readonly string CheeseUrl = $"{WwwUrl}/cheese/play/";
public static readonly string FavoritesUrl = $"{WwwUrl}/medialist/detail/";
#region 视频
/// <summary>
/// 是否为av id
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsAvId(string input)
{
return IsIntId(input, "av");
}
/// <summary>
/// 是否为av url
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsAvUrl(string input)
{
string id = GetVideoId(input);
return IsAvId(id);
}
/// <summary>
/// 获取av id
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static long GetAvId(string input)
{
if (IsAvId(input))
{
return Number.GetInt(input.Remove(0, 2));
}
else if (IsAvUrl(input))
{
return Number.GetInt(GetVideoId(input).Remove(0, 2));
}
else
{
return -1;
}
}
/// <summary>
/// 是否为bv id
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsBvId(string input)
{
return input.StartsWith("BV") && input.Length == 12;
}
/// <summary>
/// 是否为bv url
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsBvUrl(string input)
{
string id = GetVideoId(input);
return IsBvId(id);
}
/// <summary>
/// 获取bv id
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static string GetBvId(string input)
{
if (IsBvId(input))
{
return input;
}
else if (IsBvUrl(input))
{
return GetVideoId(input);
}
else
{
return "";
}
}
#endregion
#region 番剧(电影、电视剧)
/// <summary>
/// 是否为番剧season id
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsBangumiSeasonId(string input)
{
return IsIntId(input, "ss");
}
/// <summary>
/// 是否为番剧season url
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsBangumiSeasonUrl(string input)
{
string id = GetBangumiId(input);
return IsBangumiSeasonId(id);
}
/// <summary>
/// 获取番剧season id
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static long GetBangumiSeasonId(string input)
{
if (IsBangumiSeasonId(input))
{
return Number.GetInt(input.Remove(0, 2));
}
else if (IsBangumiSeasonUrl(input))
{
return Number.GetInt(GetBangumiId(input).Remove(0, 2));
}
else
{
return -1;
}
}
/// <summary>
/// 是否为番剧episode id
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsBangumiEpisodeId(string input)
{
return IsIntId(input, "ep");
}
/// <summary>
/// 是否为番剧episode url
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsBangumiEpisodeUrl(string input)
{
string id = GetBangumiId(input);
return IsBangumiEpisodeId(id);
}
/// <summary>
/// 获取番剧episode id
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static long GetBangumiEpisodeId(string input)
{
if (IsBangumiEpisodeId(input))
{
return Number.GetInt(input.Remove(0, 2));
}
else if (IsBangumiEpisodeUrl(input))
{
return Number.GetInt(GetBangumiId(input).Remove(0, 2));
}
else
{
return -1;
}
}
/// <summary>
/// 是否为番剧media id
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsBangumiMediaId(string input)
{
return IsIntId(input, "md");
}
/// <summary>
/// 是否为番剧media url
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsBangumiMediaUrl(string input)
{
string id = GetBangumiId(input);
return IsBangumiMediaId(id);
}
/// <summary>
/// 获取番剧media id
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static long GetBangumiMediaId(string input)
{
if (IsBangumiMediaId(input))
{
return Number.GetInt(input.Remove(0, 2));
}
else if (IsBangumiMediaUrl(input))
{
return Number.GetInt(GetBangumiId(input).Remove(0, 2));
}
else
{
return -1;
}
}
#endregion
#region 课程
/// <summary>
/// 是否为课程season url
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsCheeseSeasonUrl(string input)
{
string id = GetCheeseId(input);
return IsIntId(id, "ss");
}
/// <summary>
/// 获取课程season id
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static long GetCheeseSeasonId(string input)
{
return IsCheeseSeasonUrl(input) ? Number.GetInt(GetCheeseId(input).Remove(0, 2)) : -1;
}
/// <summary>
/// 是否为课程episode url
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsCheeseEpisodeUrl(string input)
{
string id = GetCheeseId(input);
return IsIntId(id, "ep");
}
/// <summary>
/// 获取课程episode id
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static long GetCheeseEpisodeId(string input)
{
return IsCheeseEpisodeUrl(input) ? Number.GetInt(GetCheeseId(input).Remove(0, 2)) : -1;
}
#endregion
#region 收藏夹
/// <summary>
/// 是否为收藏夹id
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsFavoritesId(string input)
{
return IsIntId(input, "ml");
}
/// <summary>
/// 是否为收藏夹url
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsFavoritesUrl(string input)
{
string favoritesId = GetId(input, FavoritesUrl);
return IsFavoritesId(favoritesId);
}
/// <summary>
/// 获取收藏夹id
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static long GetFavoritesId(string input)
{
if (IsFavoritesId(input))
{
return Number.GetInt(input.Remove(0, 2));
}
else if (IsFavoritesUrl(input))
{
return Number.GetInt(GetId(input, FavoritesUrl).Remove(0, 2));
}
else
{
return -1;
}
}
#endregion
#region 用户空间
/// <summary>
/// 是否为用户id
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsUserId(string input)
{
if (input.ToLower().StartsWith("uid:"))
{
return Regex.IsMatch(input.Remove(0, 4), @"^\d+$");
}
else if (input.ToLower().StartsWith("uid"))
{
return Regex.IsMatch(input.Remove(0, 3), @"^\d+$");
}
else { return false; }
}
/// <summary>
/// 是否为用户空间url
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsUserUrl(string input)
{
string baseUrl = $"{SpaceUrl}/";
string id = GetId(input, baseUrl);
return Number.IsInt(id);
}
/// <summary>
/// 获取用户mid
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static long GetUserId(string input)
{
if (input.ToLower().StartsWith("uid:"))
{
return Number.GetInt(input.Remove(0, 4));
}
else if (input.ToLower().StartsWith("uid"))
{
return Number.GetInt(input.Remove(0, 3));
}
else if (IsUserUrl(input))
{
string baseUrl = $"{SpaceUrl}/";
string id = GetId(input, baseUrl);
return Number.GetInt(id);
}
else
{
return -1;
}
}
#endregion
/// <summary>
/// 是否为网址
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private static bool IsUrl(string input)
{
return input.StartsWith("http://") || input.StartsWith("https://");
}
/// <summary>
/// 将http转为https
/// </summary>
/// <returns></returns>
private static string EnableHttps(string url)
{
if (!IsUrl(url)) { return null; }
return url.Replace("http://", "https://");
}
/// <summary>
/// 去除url中的参数
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
private static string DeleteUrlParam(string url)
{
string[] strList = url.Split('?');
return strList[0].EndsWith("/") ? strList[0].TrimEnd('/') : strList[0];
}
/// <summary>
/// 从url中获取视频idavid/bvid
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private static string GetVideoId(string input)
{
return GetId(input, VideoUrl);
}
/// <summary>
/// 从url中获取番剧idss/ep/md
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private static string GetBangumiId(string input)
{
string id = GetId(input, BangumiUrl);
if (id != "") { return id; }
return GetId(input, BangumiMediaUrl);
}
/// <summary>
/// 从url中获取课程idss/ep
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private static string GetCheeseId(string input)
{
return GetId(input, CheeseUrl);
}
/// <summary>
/// 是否为数字型id
/// </summary>
/// <param name="input"></param>
/// <param name="prefix"></param>
/// <returns></returns>
private static bool IsIntId(string input, string prefix)
{
if (input.ToLower().StartsWith(prefix))
{
return Regex.IsMatch(input.Remove(0, 2), @"^\d+$");
}
return false;
}
/// <summary>
/// 从url中获取id
/// </summary>
/// <param name="input"></param>
/// <param name="baseUrl"></param>
/// <returns></returns>
private static string GetId(string input, string baseUrl)
{
if (!IsUrl(input)) { return ""; }
string url = EnableHttps(input);
url = DeleteUrlParam(url);
url = url.Replace(MobileUrl, WwwUrl);
if (!url.StartsWith(baseUrl)) { return ""; }
return url.Replace(baseUrl, "");
}
}
}

@ -0,0 +1,70 @@
using DownKyi.Core.BiliApi.Cheese.Models;
using DownKyi.Core.Logging;
using Newtonsoft.Json;
using System;
namespace DownKyi.Core.BiliApi.Cheese
{
public static class CheeseInfo
{
/// <summary>
/// 获取课程基本信息
/// </summary>
/// <param name="seasonId"></param>
/// <param name="episodeId"></param>
/// <returns></returns>
public static CheeseView CheeseViewInfo(long seasonId = -1, long episodeId = -1)
{
string baseUrl = "https://api.bilibili.com/pugv/view/web/season";
string referer = "https://www.bilibili.com";
string url;
if (seasonId > -1) { url = $"{baseUrl}?season_id={seasonId}"; }
else if (episodeId > -1) { url = $"{baseUrl}?ep_id={episodeId}"; }
else { return null; }
string response = WebClient.RequestWeb(url, referer);
try
{
var cheese = JsonConvert.DeserializeObject<CheeseViewOrigin>(response);
if (cheese != null) { return cheese.Data; }
else { return null; }
}
catch (Exception e)
{
Utils.Debug.Console.PrintLine("CheeseViewInfo()发生异常: {0}", e);
LogManager.Error("CheeseInfo", e);
return null;
}
}
/// <summary>
/// 获取课程分集列表
/// </summary>
/// <param name="seasonId"></param>
/// <param name="ps"></param>
/// <param name="pn"></param>
/// <returns></returns>
public static CheeseEpisodeList CheeseEpisodeList(long seasonId, int ps = 50, int pn = 1)
{
string url = $"https://api.bilibili.com/pugv/view/web/ep/list?season_id={seasonId}&pn={pn}&ps={ps}";
string referer = "https://www.bilibili.com";
string response = WebClient.RequestWeb(url, referer);
try
{
var cheese = JsonConvert.DeserializeObject<CheeseEpisodeListOrigin>(response);
if (cheese != null) { return cheese.Data; }
else { return null; }
}
catch (Exception e)
{
Utils.Debug.Console.PrintLine("CheeseEpisodeList()发生异常: {0}", e);
LogManager.Error("CheeseInfo", e);
return null;
}
}
}
}

@ -0,0 +1,17 @@
using DownKyi.Core.BiliApi.Models;
using Newtonsoft.Json;
using System.Collections.Generic;
namespace DownKyi.Core.BiliApi.Cheese.Models
{
public class CheeseBrief : BaseModel
{
// content
[JsonProperty("img")]
public List<CheeseImg> Img { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("type")]
public int Type { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save