mirror of
https://github.com/cxfksword/jellyfin-plugin-danmu.git
synced 2026-04-23 18:12:00 +08:00
build: repack to one dll assembly
This commit is contained in:
26
Jellyfin.Plugin.Danmu/ILRepack.targets
Normal file
26
Jellyfin.Plugin.Danmu/ILRepack.targets
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Target Name="ILRepacker" AfterTargets="Build" Condition="'$(Configuration)'=='Release' or '$(Configuration)'=='Debug'">
|
||||
<PropertyGroup>
|
||||
<DoILRepack>false</DoILRepack>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<InputAssemblies Include="$(OutputPath)$(AssemblyName).dll" />
|
||||
<InputAssemblies Include="$(OutputPath)RateLimiter.dll" />
|
||||
<InputAssemblies Include="$(OutputPath)ComposableAsync.Core.dll" />
|
||||
<InputAssemblies Include="$(OutputPath)Google.Protobuf.dll" />
|
||||
<InputAssemblies Include="$(OutputPath)ICSharpCode.SharpZipLib.dll" />
|
||||
</ItemGroup>
|
||||
|
||||
<ILRepack
|
||||
Parallel="false"
|
||||
Internalize="true"
|
||||
DebugInfo="true"
|
||||
InputAssemblies="@(InputAssemblies)"
|
||||
LibraryPath="$(OutputPath)"
|
||||
TargetKind="Dll"
|
||||
OutputFile="$(OutputPath)$(AssemblyName).dll"
|
||||
/>
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -6,6 +6,7 @@
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<TreatWarningsAsErrors>False</TreatWarningsAsErrors>
|
||||
@@ -14,9 +15,12 @@
|
||||
<TreatWarningsAsErrors>False</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ComposableAsync.Core" Version="1.3.0" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.22.0" />
|
||||
<PackageReference Include="ILRepack.Lib.MSBuild.Minor" Version="2.1.19-alpha.2" />
|
||||
<PackageReference Include="Jellyfin.Controller" Version="10.8.0" />
|
||||
<PackageReference Include="Jellyfin.Model" Version="10.8.0" />
|
||||
<PackageReference Include="RateLimiter" Version="2.2.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("ComposableAsync.Core.Test")]
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security;
|
||||
|
||||
namespace ComposableAsync
|
||||
{
|
||||
/// <summary>
|
||||
/// Dispatcher awaiter, making a dispatcher awaitable
|
||||
/// </summary>
|
||||
public struct DispatcherAwaiter : INotifyCompletion
|
||||
{
|
||||
/// <summary>
|
||||
/// Dispatcher never is synchronous
|
||||
/// </summary>
|
||||
public bool IsCompleted => false;
|
||||
|
||||
private readonly IDispatcher _Dispatcher;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a NotifyCompletion fom a dispatcher
|
||||
/// </summary>
|
||||
/// <param name="dispatcher"></param>
|
||||
public DispatcherAwaiter(IDispatcher dispatcher)
|
||||
{
|
||||
_Dispatcher = dispatcher;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispatch on complete
|
||||
/// </summary>
|
||||
/// <param name="continuation"></param>
|
||||
[SecuritySafeCritical]
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
_Dispatcher.Dispatch(continuation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// No Result
|
||||
/// </summary>
|
||||
public void GetResult() { }
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ComposableAsync
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="DelegatingHandler"/> implementation based on <see cref="IDispatcher"/>
|
||||
/// </summary>
|
||||
internal class DispatcherDelegatingHandler : DelegatingHandler
|
||||
{
|
||||
private readonly IDispatcher _Dispatcher;
|
||||
|
||||
/// <summary>
|
||||
/// Build an <see cref="DelegatingHandler"/> from a <see cref="IDispatcher"/>
|
||||
/// </summary>
|
||||
/// <param name="dispatcher"></param>
|
||||
public DispatcherDelegatingHandler(IDispatcher dispatcher)
|
||||
{
|
||||
_Dispatcher = dispatcher;
|
||||
InnerHandler = new HttpClientHandler();
|
||||
}
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return _Dispatcher.Enqueue(() => base.SendAsync(request, cancellationToken), cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ComposableAsync
|
||||
{
|
||||
internal class ComposedDispatcher : IDispatcher, IAsyncDisposable
|
||||
{
|
||||
private readonly IDispatcher _First;
|
||||
private readonly IDispatcher _Second;
|
||||
|
||||
public ComposedDispatcher(IDispatcher first, IDispatcher second)
|
||||
{
|
||||
_First = first;
|
||||
_Second = second;
|
||||
}
|
||||
|
||||
public void Dispatch(Action action)
|
||||
{
|
||||
_First.Dispatch(() => _Second.Dispatch(action));
|
||||
}
|
||||
|
||||
public async Task Enqueue(Action action)
|
||||
{
|
||||
await _First.Enqueue(() => _Second.Enqueue(action));
|
||||
}
|
||||
|
||||
public async Task<T> Enqueue<T>(Func<T> action)
|
||||
{
|
||||
return await _First.Enqueue(() => _Second.Enqueue(action));
|
||||
}
|
||||
|
||||
public async Task Enqueue(Func<Task> action)
|
||||
{
|
||||
await _First.Enqueue(() => _Second.Enqueue(action));
|
||||
}
|
||||
|
||||
public async Task<T> Enqueue<T>(Func<Task<T>> action)
|
||||
{
|
||||
return await _First.Enqueue(() => _Second.Enqueue(action));
|
||||
}
|
||||
|
||||
public async Task Enqueue(Func<Task> action, CancellationToken cancellationToken)
|
||||
{
|
||||
await _First.Enqueue(() => _Second.Enqueue(action, cancellationToken), cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<T> Enqueue<T>(Func<Task<T>> action, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _First.Enqueue(() => _Second.Enqueue(action, cancellationToken), cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<T> Enqueue<T>(Func<T> action, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _First.Enqueue(() => _Second.Enqueue(action, cancellationToken), cancellationToken);
|
||||
}
|
||||
|
||||
public async Task Enqueue(Action action, CancellationToken cancellationToken)
|
||||
{
|
||||
await _First.Enqueue(() => _Second.Enqueue(action, cancellationToken), cancellationToken);
|
||||
}
|
||||
|
||||
public IDispatcher Clone() => new ComposedDispatcher(_First, _Second);
|
||||
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
return Task.WhenAll(DisposeAsync(_First), DisposeAsync(_Second));
|
||||
}
|
||||
|
||||
private static Task DisposeAsync(IDispatcher disposable) => (disposable as IAsyncDisposable)?.DisposeAsync() ?? Task.CompletedTask;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ComposableAsync
|
||||
{
|
||||
internal class DispatcherAdapter : IDispatcher
|
||||
{
|
||||
private readonly IBasicDispatcher _BasicDispatcher;
|
||||
|
||||
public DispatcherAdapter(IBasicDispatcher basicDispatcher)
|
||||
{
|
||||
_BasicDispatcher = basicDispatcher;
|
||||
}
|
||||
|
||||
public IDispatcher Clone() => new DispatcherAdapter(_BasicDispatcher.Clone());
|
||||
|
||||
public void Dispatch(Action action)
|
||||
{
|
||||
_BasicDispatcher.Enqueue(action, CancellationToken.None);
|
||||
}
|
||||
|
||||
public Task Enqueue(Action action)
|
||||
{
|
||||
return _BasicDispatcher.Enqueue(action, CancellationToken.None);
|
||||
}
|
||||
|
||||
public Task<T> Enqueue<T>(Func<T> action)
|
||||
{
|
||||
return _BasicDispatcher.Enqueue(action, CancellationToken.None);
|
||||
}
|
||||
|
||||
public Task Enqueue(Func<Task> action)
|
||||
{
|
||||
return _BasicDispatcher.Enqueue(action, CancellationToken.None);
|
||||
}
|
||||
|
||||
public Task<T> Enqueue<T>(Func<Task<T>> action)
|
||||
{
|
||||
return _BasicDispatcher.Enqueue(action, CancellationToken.None);
|
||||
}
|
||||
|
||||
public Task<T> Enqueue<T>(Func<T> action, CancellationToken cancellationToken)
|
||||
{
|
||||
return _BasicDispatcher.Enqueue(action, cancellationToken);
|
||||
}
|
||||
|
||||
public Task Enqueue(Action action, CancellationToken cancellationToken)
|
||||
{
|
||||
return _BasicDispatcher.Enqueue(action, cancellationToken);
|
||||
}
|
||||
|
||||
public Task Enqueue(Func<Task> action, CancellationToken cancellationToken)
|
||||
{
|
||||
return _BasicDispatcher.Enqueue(action, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<T> Enqueue<T>(Func<Task<T>> action, CancellationToken cancellationToken)
|
||||
{
|
||||
return _BasicDispatcher.Enqueue(action, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ComposableAsync
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IDispatcher"/> that run actions synchronously
|
||||
/// </summary>
|
||||
public sealed class NullDispatcher: IDispatcher
|
||||
{
|
||||
private NullDispatcher() { }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a static null dispatcher
|
||||
/// </summary>
|
||||
public static IDispatcher Instance { get; } = new NullDispatcher();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispatch(Action action)
|
||||
{
|
||||
action();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task Enqueue(Action action)
|
||||
{
|
||||
action();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<T> Enqueue<T>(Func<T> action)
|
||||
{
|
||||
return Task.FromResult(action());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task Enqueue(Func<Task> action)
|
||||
{
|
||||
await action();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<T> Enqueue<T>(Func<Task<T>> action)
|
||||
{
|
||||
return await action();
|
||||
}
|
||||
|
||||
public Task<T> Enqueue<T>(Func<T> action, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(action());
|
||||
}
|
||||
|
||||
public Task Enqueue(Action action, CancellationToken cancellationToken)
|
||||
{
|
||||
action();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task Enqueue(Func<Task> action, CancellationToken cancellationToken)
|
||||
{
|
||||
await action();
|
||||
}
|
||||
|
||||
public async Task<T> Enqueue<T>(Func<Task<T>> action, CancellationToken cancellationToken)
|
||||
{
|
||||
return await action();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDispatcher Clone() => Instance;
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace ComposableAsync
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IDispatcher"/> extension methods provider
|
||||
/// </summary>
|
||||
public static class DispatcherExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns awaitable to enter in the dispatcher context
|
||||
/// This extension method make a dispatcher awaitable
|
||||
/// </summary>
|
||||
/// <param name="dispatcher"></param>
|
||||
/// <returns></returns>
|
||||
public static DispatcherAwaiter GetAwaiter(this IDispatcher dispatcher)
|
||||
{
|
||||
return new DispatcherAwaiter(dispatcher);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a composed dispatcher applying the given dispatcher
|
||||
/// after the first one
|
||||
/// </summary>
|
||||
/// <param name="dispatcher"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
public static IDispatcher Then(this IDispatcher dispatcher, IDispatcher other)
|
||||
{
|
||||
if (dispatcher == null)
|
||||
throw new ArgumentNullException(nameof(dispatcher));
|
||||
|
||||
if (other == null)
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
|
||||
return new ComposedDispatcher(dispatcher, other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a composed dispatcher applying the given dispatchers sequentially
|
||||
/// </summary>
|
||||
/// <param name="dispatcher"></param>
|
||||
/// <param name="others"></param>
|
||||
/// <returns></returns>
|
||||
public static IDispatcher Then(this IDispatcher dispatcher, params IDispatcher[] others)
|
||||
{
|
||||
return dispatcher.Then((IEnumerable<IDispatcher>)others);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a composed dispatcher applying the given dispatchers sequentially
|
||||
/// </summary>
|
||||
/// <param name="dispatcher"></param>
|
||||
/// <param name="others"></param>
|
||||
/// <returns></returns>
|
||||
public static IDispatcher Then(this IDispatcher dispatcher, IEnumerable<IDispatcher> others)
|
||||
{
|
||||
if (dispatcher == null)
|
||||
throw new ArgumentNullException(nameof(dispatcher));
|
||||
|
||||
if (others == null)
|
||||
throw new ArgumentNullException(nameof(others));
|
||||
|
||||
return others.Aggregate(dispatcher, (cum, val) => cum.Then(val));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="DelegatingHandler"/> from an <see cref="IDispatcher"/>
|
||||
/// </summary>
|
||||
/// <param name="dispatcher"></param>
|
||||
/// <returns></returns>
|
||||
public static DelegatingHandler AsDelegatingHandler(this IDispatcher dispatcher)
|
||||
{
|
||||
return new DispatcherDelegatingHandler(dispatcher);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="IDispatcher"/> from an <see cref="IBasicDispatcher"/>
|
||||
/// </summary>
|
||||
/// <param name="basicDispatcher"></param>
|
||||
/// <returns></returns>
|
||||
public static IDispatcher ToFullDispatcher(this IBasicDispatcher @basicDispatcher)
|
||||
{
|
||||
return new DispatcherAdapter(@basicDispatcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
namespace ComposableAsync
|
||||
{
|
||||
/// <summary>
|
||||
/// Dispatcher manager
|
||||
/// </summary>
|
||||
public interface IDispatcherManager : IAsyncDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// true if the Dispatcher should be released
|
||||
/// </summary>
|
||||
bool DisposeDispatcher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a consumable Dispatcher
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IDispatcher GetDispatcher();
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ComposableAsync
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IDispatcherManager"/> implementation based on single <see cref="IDispatcher"/>
|
||||
/// </summary>
|
||||
public sealed class MonoDispatcherManager : IDispatcherManager
|
||||
{
|
||||
/// <inheritdoc cref="IDispatcherManager"/>
|
||||
public bool DisposeDispatcher { get; }
|
||||
|
||||
/// <inheritdoc cref="IDispatcherManager"/>
|
||||
public IDispatcher GetDispatcher() => _Dispatcher;
|
||||
|
||||
private readonly IDispatcher _Dispatcher;
|
||||
|
||||
/// <summary>
|
||||
/// Create
|
||||
/// </summary>
|
||||
/// <param name="dispatcher"></param>
|
||||
/// <param name="shouldDispose"></param>
|
||||
public MonoDispatcherManager(IDispatcher dispatcher, bool shouldDispose = false)
|
||||
{
|
||||
_Dispatcher = dispatcher;
|
||||
DisposeDispatcher = shouldDispose;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IDispatcherManager"/>
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
return DisposeDispatcher && (_Dispatcher is IAsyncDisposable disposable) ?
|
||||
disposable.DisposeAsync() : Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
namespace ComposableAsync
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IDispatcherProvider"/> extension
|
||||
/// </summary>
|
||||
public static class DispatcherProviderExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the underlying <see cref="IDispatcher"/>
|
||||
/// </summary>
|
||||
/// <param name="dispatcherProvider"></param>
|
||||
/// <returns></returns>
|
||||
public static IDispatcher GetAssociatedDispatcher(this IDispatcherProvider dispatcherProvider)
|
||||
{
|
||||
return dispatcherProvider?.Dispatcher ?? NullDispatcher.Instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ComposableAsync
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IAsyncDisposable"/> implementation aggregating other <see cref="IAsyncDisposable"/>
|
||||
/// </summary>
|
||||
public sealed class ComposableAsyncDisposable : IAsyncDisposable
|
||||
{
|
||||
private readonly ConcurrentQueue<IAsyncDisposable> _Disposables;
|
||||
|
||||
/// <summary>
|
||||
/// Build an empty ComposableAsyncDisposable
|
||||
/// </summary>
|
||||
public ComposableAsyncDisposable()
|
||||
{
|
||||
_Disposables = new ConcurrentQueue<IAsyncDisposable>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an <see cref="IAsyncDisposable"/> to the ComposableAsyncDisposable
|
||||
/// and returns it
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="disposable"></param>
|
||||
/// <returns></returns>
|
||||
public T Add<T>(T disposable) where T: IAsyncDisposable
|
||||
{
|
||||
if (disposable == null)
|
||||
return default(T);
|
||||
|
||||
_Disposables.Enqueue(disposable);
|
||||
return disposable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose all the resources asynchronously
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
var tasks = _Disposables.ToArray().Select(disposable => disposable.DisposeAsync()).ToArray();
|
||||
return Task.WhenAll(tasks);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ComposableAsync
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronous version of IDisposable
|
||||
/// For reference see discussion: https://github.com/dotnet/roslyn/issues/114
|
||||
/// </summary>
|
||||
public interface IAsyncDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Performs asynchronously application-defined tasks associated with freeing,
|
||||
/// releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
Task DisposeAsync();
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ComposableAsync
|
||||
{
|
||||
/// <summary>
|
||||
/// Simplified version of <see cref="IDispatcher"/> that can be converted
|
||||
/// to a <see cref="IDispatcher"/> using the ToFullDispatcher extension method
|
||||
/// </summary>
|
||||
public interface IBasicDispatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Clone dispatcher
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IBasicDispatcher Clone();
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue the function and return a task corresponding
|
||||
/// to the execution of the task
|
||||
/// /// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task<T> Enqueue<T>(Func<T> action, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue the action and return a task corresponding
|
||||
/// to the execution of the task
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task Enqueue(Action action, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue the task and return a task corresponding
|
||||
/// to the execution of the task
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task Enqueue(Func<Task> action, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue the task and return a task corresponding
|
||||
/// to the execution of the original task
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task<T> Enqueue<T>(Func<Task<T>> action, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ComposableAsync
|
||||
{
|
||||
/// <summary>
|
||||
/// Dispatcher executes an action or a function
|
||||
/// on its own context
|
||||
/// </summary>
|
||||
public interface IDispatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Execute action on dispatcher context in a
|
||||
/// none-blocking way
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
void Dispatch(Action action);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue the action and return a task corresponding to
|
||||
/// the completion of the action
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
Task Enqueue(Action action);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue the function and return a task corresponding to
|
||||
/// the result of the function
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
Task<T> Enqueue<T>(Func<T> action);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue the task and return a task corresponding to
|
||||
/// the completion of the task
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
Task Enqueue(Func<Task> action);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue the task and return a task corresponding
|
||||
/// to the execution of the original task
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
Task<T> Enqueue<T>(Func<Task<T>> action);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue the function and return a task corresponding
|
||||
/// to the execution of the task
|
||||
/// /// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task<T> Enqueue<T>(Func<T> action, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue the action and return a task corresponding
|
||||
/// to the execution of the task
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task Enqueue(Action action, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue the task and return a task corresponding
|
||||
/// to the execution of the task
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task Enqueue(Func<Task> action, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue the task and return a task corresponding
|
||||
/// to the execution of the original task
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task<T> Enqueue<T>(Func<Task<T>> action, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Clone dispatcher
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IDispatcher Clone();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace ComposableAsync
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the fiber associated with an actor
|
||||
/// </summary>
|
||||
public interface IDispatcherProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the corresponding <see cref="IDispatcher"/>
|
||||
/// </summary>
|
||||
IDispatcher Dispatcher { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
namespace RateLimiter
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension to interface <see cref="IAwaitableConstraint"/>
|
||||
/// </summary>
|
||||
public static class AwaitableConstraintExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Compose two awaitable constraint in a new one
|
||||
/// </summary>
|
||||
/// <param name="awaitableConstraint1"></param>
|
||||
/// <param name="awaitableConstraint2"></param>
|
||||
/// <returns></returns>
|
||||
public static IAwaitableConstraint Compose(this IAwaitableConstraint awaitableConstraint1, IAwaitableConstraint awaitableConstraint2)
|
||||
{
|
||||
if (awaitableConstraint1 == awaitableConstraint2)
|
||||
return awaitableConstraint1;
|
||||
|
||||
return new ComposedAwaitableConstraint(awaitableConstraint1, awaitableConstraint2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RateLimiter
|
||||
{
|
||||
internal class ComposedAwaitableConstraint : IAwaitableConstraint
|
||||
{
|
||||
private readonly IAwaitableConstraint _AwaitableConstraint1;
|
||||
private readonly IAwaitableConstraint _AwaitableConstraint2;
|
||||
private readonly SemaphoreSlim _Semaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
internal ComposedAwaitableConstraint(IAwaitableConstraint awaitableConstraint1, IAwaitableConstraint awaitableConstraint2)
|
||||
{
|
||||
_AwaitableConstraint1 = awaitableConstraint1;
|
||||
_AwaitableConstraint2 = awaitableConstraint2;
|
||||
}
|
||||
|
||||
public IAwaitableConstraint Clone()
|
||||
{
|
||||
return new ComposedAwaitableConstraint(_AwaitableConstraint1.Clone(), _AwaitableConstraint2.Clone());
|
||||
}
|
||||
|
||||
public async Task<IDisposable> WaitForReadiness(CancellationToken cancellationToken)
|
||||
{
|
||||
await _Semaphore.WaitAsync(cancellationToken);
|
||||
IDisposable[] disposables;
|
||||
try
|
||||
{
|
||||
disposables = await Task.WhenAll(_AwaitableConstraint1.WaitForReadiness(cancellationToken), _AwaitableConstraint2.WaitForReadiness(cancellationToken));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_Semaphore.Release();
|
||||
throw;
|
||||
}
|
||||
return new DisposeAction(() =>
|
||||
{
|
||||
foreach (var disposable in disposables)
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
_Semaphore.Release();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RateLimiter
|
||||
{
|
||||
/// <summary>
|
||||
/// Provide an awaitable constraint based on number of times per duration
|
||||
/// </summary>
|
||||
public class CountByIntervalAwaitableConstraint : IAwaitableConstraint
|
||||
{
|
||||
/// <summary>
|
||||
/// List of the last time stamps
|
||||
/// </summary>
|
||||
public IReadOnlyList<DateTime> TimeStamps => _TimeStamps.ToList();
|
||||
|
||||
/// <summary>
|
||||
/// Stack of the last time stamps
|
||||
/// </summary>
|
||||
protected LimitedSizeStack<DateTime> _TimeStamps { get; }
|
||||
|
||||
private int _Count { get; }
|
||||
private TimeSpan _TimeSpan { get; }
|
||||
private SemaphoreSlim _Semaphore { get; } = new SemaphoreSlim(1, 1);
|
||||
private ITime _Time { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new AwaitableConstraint based on number of times per duration
|
||||
/// </summary>
|
||||
/// <param name="count"></param>
|
||||
/// <param name="timeSpan"></param>
|
||||
public CountByIntervalAwaitableConstraint(int count, TimeSpan timeSpan) : this(count, timeSpan, TimeSystem.StandardTime)
|
||||
{
|
||||
}
|
||||
|
||||
internal CountByIntervalAwaitableConstraint(int count, TimeSpan timeSpan, ITime time)
|
||||
{
|
||||
if (count <= 0)
|
||||
throw new ArgumentException("count should be strictly positive", nameof(count));
|
||||
|
||||
if (timeSpan.TotalMilliseconds <= 0)
|
||||
throw new ArgumentException("timeSpan should be strictly positive", nameof(timeSpan));
|
||||
|
||||
_Count = count;
|
||||
_TimeSpan = timeSpan;
|
||||
_TimeStamps = new LimitedSizeStack<DateTime>(_Count);
|
||||
_Time = time;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns a task that will complete once the constraint is fulfilled
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">
|
||||
/// Cancel the wait
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A disposable that should be disposed upon task completion
|
||||
/// </returns>
|
||||
public async Task<IDisposable> WaitForReadiness(CancellationToken cancellationToken)
|
||||
{
|
||||
await _Semaphore.WaitAsync(cancellationToken);
|
||||
var count = 0;
|
||||
var now = _Time.GetNow();
|
||||
var target = now - _TimeSpan;
|
||||
LinkedListNode<DateTime> element = _TimeStamps.First, last = null;
|
||||
while ((element != null) && (element.Value > target))
|
||||
{
|
||||
last = element;
|
||||
element = element.Next;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count < _Count)
|
||||
return new DisposeAction(OnEnded);
|
||||
|
||||
Debug.Assert(element == null);
|
||||
Debug.Assert(last != null);
|
||||
var timeToWait = last.Value.Add(_TimeSpan) - now;
|
||||
try
|
||||
{
|
||||
await _Time.GetDelay(timeToWait, cancellationToken);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_Semaphore.Release();
|
||||
throw;
|
||||
}
|
||||
|
||||
return new DisposeAction(OnEnded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clone CountByIntervalAwaitableConstraint
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IAwaitableConstraint Clone()
|
||||
{
|
||||
return new CountByIntervalAwaitableConstraint(_Count, _TimeSpan, _Time);
|
||||
}
|
||||
|
||||
private void OnEnded()
|
||||
{
|
||||
var now = _Time.GetNow();
|
||||
_TimeStamps.Push(now);
|
||||
OnEnded(now);
|
||||
_Semaphore.Release();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when action has been executed
|
||||
/// </summary>
|
||||
/// <param name="now"></param>
|
||||
protected virtual void OnEnded(DateTime now)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace RateLimiter
|
||||
{
|
||||
internal class DisposeAction : IDisposable
|
||||
{
|
||||
private Action _Act;
|
||||
|
||||
public DisposeAction(Action act)
|
||||
{
|
||||
_Act = act;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_Act?.Invoke();
|
||||
_Act = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RateLimiter
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a time constraints that can be awaited
|
||||
/// </summary>
|
||||
public interface IAwaitableConstraint
|
||||
{
|
||||
/// <summary>
|
||||
/// returns a task that will complete once the constraint is fulfilled
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">
|
||||
/// Cancel the wait
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A disposable that should be disposed upon task completion
|
||||
/// </returns>
|
||||
Task<IDisposable> WaitForReadiness(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new IAwaitableConstraint with same constraints but unused
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IAwaitableConstraint Clone();
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RateLimiter
|
||||
{
|
||||
/// <summary>
|
||||
/// Time abstraction
|
||||
/// </summary>
|
||||
internal interface ITime
|
||||
{
|
||||
/// <summary>
|
||||
/// Return Now DateTime
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
DateTime GetNow();
|
||||
|
||||
/// <summary>
|
||||
/// Returns a task delay
|
||||
/// </summary>
|
||||
/// <param name="timespan"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task GetDelay(TimeSpan timespan, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RateLimiter
|
||||
{
|
||||
/// <summary>
|
||||
/// LinkedList with a limited size
|
||||
/// If the size exceeds the limit older entry are removed
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class LimitedSizeStack<T>: LinkedList<T>
|
||||
{
|
||||
private readonly int _MaxSize;
|
||||
|
||||
/// <summary>
|
||||
/// Construct the LimitedSizeStack with the given limit
|
||||
/// </summary>
|
||||
/// <param name="maxSize"></param>
|
||||
public LimitedSizeStack(int maxSize)
|
||||
{
|
||||
_MaxSize = maxSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Push new entry. If he size exceeds the limit, the oldest entry is removed
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
public void Push(T item)
|
||||
{
|
||||
AddFirst(item);
|
||||
|
||||
if (Count > _MaxSize)
|
||||
RemoveLast();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace RateLimiter
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="CountByIntervalAwaitableConstraint"/> that is able to save own state.
|
||||
/// </summary>
|
||||
public sealed class PersistentCountByIntervalAwaitableConstraint : CountByIntervalAwaitableConstraint
|
||||
{
|
||||
private readonly Action<DateTime> _SaveStateAction;
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of <see cref="PersistentCountByIntervalAwaitableConstraint"/>.
|
||||
/// </summary>
|
||||
/// <param name="count">Maximum actions allowed per time interval.</param>
|
||||
/// <param name="timeSpan">Time interval limits are applied for.</param>
|
||||
/// <param name="saveStateAction">Action is used to save state.</param>
|
||||
/// <param name="initialTimeStamps">Initial timestamps.</param>
|
||||
public PersistentCountByIntervalAwaitableConstraint(int count, TimeSpan timeSpan,
|
||||
Action<DateTime> saveStateAction, IEnumerable<DateTime> initialTimeStamps) : base(count, timeSpan)
|
||||
{
|
||||
_SaveStateAction = saveStateAction;
|
||||
|
||||
if (initialTimeStamps == null)
|
||||
return;
|
||||
|
||||
foreach (var timeStamp in initialTimeStamps)
|
||||
{
|
||||
_TimeStamps.Push(timeStamp);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save state
|
||||
/// </summary>
|
||||
protected override void OnEnded(DateTime now)
|
||||
{
|
||||
_SaveStateAction(now);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ComposableAsync;
|
||||
|
||||
namespace RateLimiter
|
||||
{
|
||||
/// <summary>
|
||||
/// TimeLimiter implementation
|
||||
/// </summary>
|
||||
public class TimeLimiter : IDispatcher
|
||||
{
|
||||
private readonly IAwaitableConstraint _AwaitableConstraint;
|
||||
|
||||
internal TimeLimiter(IAwaitableConstraint awaitableConstraint)
|
||||
{
|
||||
_AwaitableConstraint = awaitableConstraint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform the given task respecting the time constraint
|
||||
/// returning the result of given function
|
||||
/// </summary>
|
||||
/// <param name="perform"></param>
|
||||
/// <returns></returns>
|
||||
public Task Enqueue(Func<Task> perform)
|
||||
{
|
||||
return Enqueue(perform, CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform the given task respecting the time constraint
|
||||
/// returning the result of given function
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="perform"></param>
|
||||
/// <returns></returns>
|
||||
public Task<T> Enqueue<T>(Func<Task<T>> perform)
|
||||
{
|
||||
return Enqueue(perform, CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform the given task respecting the time constraint
|
||||
/// </summary>
|
||||
/// <param name="perform"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task Enqueue(Func<Task> perform, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
using (await _AwaitableConstraint.WaitForReadiness(cancellationToken))
|
||||
{
|
||||
await perform();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform the given task respecting the time constraint
|
||||
/// returning the result of given function
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="perform"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<T> Enqueue<T>(Func<Task<T>> perform, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
using (await _AwaitableConstraint.WaitForReadiness(cancellationToken))
|
||||
{
|
||||
return await perform();
|
||||
}
|
||||
}
|
||||
|
||||
public IDispatcher Clone() => new TimeLimiter(_AwaitableConstraint.Clone());
|
||||
|
||||
private static Func<Task> Transform(Action act)
|
||||
{
|
||||
return () => { act(); return Task.FromResult(0); };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform the given task respecting the time constraint
|
||||
/// returning the result of given function
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="compute"></param>
|
||||
/// <returns></returns>
|
||||
private static Func<Task<T>> Transform<T>(Func<T> compute)
|
||||
{
|
||||
return () => Task.FromResult(compute());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform the given task respecting the time constraint
|
||||
/// </summary>
|
||||
/// <param name="perform"></param>
|
||||
/// <returns></returns>
|
||||
public Task Enqueue(Action perform)
|
||||
{
|
||||
var transformed = Transform(perform);
|
||||
return Enqueue(transformed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform the given task respecting the time constraint
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
public void Dispatch(Action action)
|
||||
{
|
||||
Enqueue(action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform the given task respecting the time constraint
|
||||
/// returning the result of given function
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="perform"></param>
|
||||
/// <returns></returns>
|
||||
public Task<T> Enqueue<T>(Func<T> perform)
|
||||
{
|
||||
var transformed = Transform(perform);
|
||||
return Enqueue(transformed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform the given task respecting the time constraint
|
||||
/// returning the result of given function
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="perform"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public Task<T> Enqueue<T>(Func<T> perform, CancellationToken cancellationToken)
|
||||
{
|
||||
var transformed = Transform(perform);
|
||||
return Enqueue(transformed, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform the given task respecting the time constraint
|
||||
/// </summary>
|
||||
/// <param name="perform"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public Task Enqueue(Action perform, CancellationToken cancellationToken)
|
||||
{
|
||||
var transformed = Transform(perform);
|
||||
return Enqueue(transformed, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a TimeLimiter based on a maximum number of times
|
||||
/// during a given period
|
||||
/// </summary>
|
||||
/// <param name="maxCount"></param>
|
||||
/// <param name="timeSpan"></param>
|
||||
/// <returns></returns>
|
||||
public static TimeLimiter GetFromMaxCountByInterval(int maxCount, TimeSpan timeSpan)
|
||||
{
|
||||
return new TimeLimiter(new CountByIntervalAwaitableConstraint(maxCount, timeSpan));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create <see cref="TimeLimiter"/> that will save state using action passed through <paramref name="saveStateAction"/> parameter.
|
||||
/// </summary>
|
||||
/// <param name="maxCount">Maximum actions allowed per time interval.</param>
|
||||
/// <param name="timeSpan">Time interval limits are applied for.</param>
|
||||
/// <param name="saveStateAction">Action is used to save state.</param>
|
||||
/// <returns><see cref="TimeLimiter"/> instance with <see cref="PersistentCountByIntervalAwaitableConstraint"/>.</returns>
|
||||
public static TimeLimiter GetPersistentTimeLimiter(int maxCount, TimeSpan timeSpan,
|
||||
Action<DateTime> saveStateAction)
|
||||
{
|
||||
return GetPersistentTimeLimiter(maxCount, timeSpan, saveStateAction, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create <see cref="TimeLimiter"/> with initial timestamps that will save state using action passed through <paramref name="saveStateAction"/> parameter.
|
||||
/// </summary>
|
||||
/// <param name="maxCount">Maximum actions allowed per time interval.</param>
|
||||
/// <param name="timeSpan">Time interval limits are applied for.</param>
|
||||
/// <param name="saveStateAction">Action is used to save state.</param>
|
||||
/// <param name="initialTimeStamps">Initial timestamps.</param>
|
||||
/// <returns><see cref="TimeLimiter"/> instance with <see cref="PersistentCountByIntervalAwaitableConstraint"/>.</returns>
|
||||
public static TimeLimiter GetPersistentTimeLimiter(int maxCount, TimeSpan timeSpan,
|
||||
Action<DateTime> saveStateAction, IEnumerable<DateTime> initialTimeStamps)
|
||||
{
|
||||
return new TimeLimiter(new PersistentCountByIntervalAwaitableConstraint(maxCount, timeSpan, saveStateAction, initialTimeStamps));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compose various IAwaitableConstraint in a TimeLimiter
|
||||
/// </summary>
|
||||
/// <param name="constraints"></param>
|
||||
/// <returns></returns>
|
||||
public static TimeLimiter Compose(params IAwaitableConstraint[] constraints)
|
||||
{
|
||||
var composed = constraints.Aggregate(default(IAwaitableConstraint),
|
||||
(accumulated, current) => (accumulated == null) ? current : accumulated.Compose(current));
|
||||
return new TimeLimiter(composed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RateLimiter
|
||||
{
|
||||
internal class TimeSystem : ITime
|
||||
{
|
||||
public static ITime StandardTime { get; }
|
||||
|
||||
static TimeSystem()
|
||||
{
|
||||
StandardTime = new TimeSystem();
|
||||
}
|
||||
|
||||
private TimeSystem()
|
||||
{
|
||||
}
|
||||
|
||||
DateTime ITime.GetNow()
|
||||
{
|
||||
return DateTime.Now;
|
||||
}
|
||||
|
||||
Task ITime.GetDelay(TimeSpan timespan, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.Delay(timespan, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,11 +74,7 @@ ass格式:
|
||||
|
||||
```sh
|
||||
dotnet restore
|
||||
dotnet publish --output=artifacts Jellyfin.Plugin.Danmu/Jellyfin.Plugin.Danmu.csproj
|
||||
|
||||
# remove unused dll
|
||||
cd artifacts
|
||||
rm -rf MediaBrowser*.dll Microsoft*.dll Newtonsoft*.dll System*.dll Emby*.dll Jellyfin.Data*.dll Jellyfin.Extensions*.dll *.json *.pdb
|
||||
dotnet publish Jellyfin.Plugin.Danmu/Jellyfin.Plugin.Danmu.csproj
|
||||
```
|
||||
|
||||
|
||||
@@ -86,7 +82,7 @@ rm -rf MediaBrowser*.dll Microsoft*.dll Newtonsoft*.dll System*.dll Emby*.dll Je
|
||||
|
||||
1. Build the plugin
|
||||
|
||||
2. Create a folder, like `danmu` and copy `artifacts/*.dll` into it
|
||||
2. Create a folder, like `danmu` and copy `./Jellyfin.Plugin.Danmu/bin/Debug/net6.0/Jellyfin.Plugin.Danmu.dll` into it
|
||||
|
||||
3. Move folder `danmu` to jellyfin `data/plugins` folder
|
||||
|
||||
|
||||
Reference in New Issue
Block a user