diff --git a/Jellyfin.Plugin.Danmu/ILRepack.targets b/Jellyfin.Plugin.Danmu/ILRepack.targets
new file mode 100644
index 0000000..ca1d1f9
--- /dev/null
+++ b/Jellyfin.Plugin.Danmu/ILRepack.targets
@@ -0,0 +1,26 @@
+
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Jellyfin.Plugin.Danmu/Jellyfin.Plugin.Danmu.csproj b/Jellyfin.Plugin.Danmu/Jellyfin.Plugin.Danmu.csproj
index 2aeae20..7305dbe 100644
--- a/Jellyfin.Plugin.Danmu/Jellyfin.Plugin.Danmu.csproj
+++ b/Jellyfin.Plugin.Danmu/Jellyfin.Plugin.Danmu.csproj
@@ -6,6 +6,7 @@
true
enable
AllEnabledByDefault
+ true
False
@@ -14,9 +15,12 @@
False
+
+
+
diff --git a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/AssemblyInfo.cs b/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/AssemblyInfo.cs
deleted file mode 100644
index 1563afa..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/AssemblyInfo.cs
+++ /dev/null
@@ -1,4 +0,0 @@
-using System.Runtime.CompilerServices;
-
-[assembly: InternalsVisibleTo("ComposableAsync.Core.Test")]
-
diff --git a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Awaitable/DispatcherAwaiter.cs b/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Awaitable/DispatcherAwaiter.cs
deleted file mode 100644
index 061840a..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Awaitable/DispatcherAwaiter.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using System;
-using System.Runtime.CompilerServices;
-using System.Security;
-
-namespace ComposableAsync
-{
- ///
- /// Dispatcher awaiter, making a dispatcher awaitable
- ///
- public struct DispatcherAwaiter : INotifyCompletion
- {
- ///
- /// Dispatcher never is synchronous
- ///
- public bool IsCompleted => false;
-
- private readonly IDispatcher _Dispatcher;
-
- ///
- /// Construct a NotifyCompletion fom a dispatcher
- ///
- ///
- public DispatcherAwaiter(IDispatcher dispatcher)
- {
- _Dispatcher = dispatcher;
- }
-
- ///
- /// Dispatch on complete
- ///
- ///
- [SecuritySafeCritical]
- public void OnCompleted(Action continuation)
- {
- _Dispatcher.Dispatch(continuation);
- }
-
- ///
- /// No Result
- ///
- public void GetResult() { }
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/DelegatingHandler/DispatcherDelegatingHandler.cs b/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/DelegatingHandler/DispatcherDelegatingHandler.cs
deleted file mode 100644
index dbd8b49..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/DelegatingHandler/DispatcherDelegatingHandler.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System.Net.Http;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace ComposableAsync
-{
- ///
- /// A implementation based on
- ///
- internal class DispatcherDelegatingHandler : DelegatingHandler
- {
- private readonly IDispatcher _Dispatcher;
-
- ///
- /// Build an from a
- ///
- ///
- public DispatcherDelegatingHandler(IDispatcher dispatcher)
- {
- _Dispatcher = dispatcher;
- InnerHandler = new HttpClientHandler();
- }
-
- protected override Task SendAsync(HttpRequestMessage request,
- CancellationToken cancellationToken)
- {
- return _Dispatcher.Enqueue(() => base.SendAsync(request, cancellationToken), cancellationToken);
- }
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Dispatcher/ComposedDispatcher.cs b/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Dispatcher/ComposedDispatcher.cs
deleted file mode 100644
index aed2434..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Dispatcher/ComposedDispatcher.cs
+++ /dev/null
@@ -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 Enqueue(Func action)
- {
- return await _First.Enqueue(() => _Second.Enqueue(action));
- }
-
- public async Task Enqueue(Func action)
- {
- await _First.Enqueue(() => _Second.Enqueue(action));
- }
-
- public async Task Enqueue(Func> action)
- {
- return await _First.Enqueue(() => _Second.Enqueue(action));
- }
-
- public async Task Enqueue(Func action, CancellationToken cancellationToken)
- {
- await _First.Enqueue(() => _Second.Enqueue(action, cancellationToken), cancellationToken);
- }
-
- public async Task Enqueue(Func> action, CancellationToken cancellationToken)
- {
- return await _First.Enqueue(() => _Second.Enqueue(action, cancellationToken), cancellationToken);
- }
-
- public async Task Enqueue(Func 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;
-
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Dispatcher/DispatcherAdapter.cs b/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Dispatcher/DispatcherAdapter.cs
deleted file mode 100644
index d6532ad..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Dispatcher/DispatcherAdapter.cs
+++ /dev/null
@@ -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 Enqueue(Func action)
- {
- return _BasicDispatcher.Enqueue(action, CancellationToken.None);
- }
-
- public Task Enqueue(Func action)
- {
- return _BasicDispatcher.Enqueue(action, CancellationToken.None);
- }
-
- public Task Enqueue(Func> action)
- {
- return _BasicDispatcher.Enqueue(action, CancellationToken.None);
- }
-
- public Task Enqueue(Func action, CancellationToken cancellationToken)
- {
- return _BasicDispatcher.Enqueue(action, cancellationToken);
- }
-
- public Task Enqueue(Action action, CancellationToken cancellationToken)
- {
- return _BasicDispatcher.Enqueue(action, cancellationToken);
- }
-
- public Task Enqueue(Func action, CancellationToken cancellationToken)
- {
- return _BasicDispatcher.Enqueue(action, cancellationToken);
- }
-
- public Task Enqueue(Func> action, CancellationToken cancellationToken)
- {
- return _BasicDispatcher.Enqueue(action, cancellationToken);
- }
- }
-}
\ No newline at end of file
diff --git a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Dispatcher/NullDispatcher.cs b/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Dispatcher/NullDispatcher.cs
deleted file mode 100644
index 4882f9e..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Dispatcher/NullDispatcher.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace ComposableAsync
-{
- ///
- /// that run actions synchronously
- ///
- public sealed class NullDispatcher: IDispatcher
- {
- private NullDispatcher() { }
-
- ///
- /// Returns a static null dispatcher
- ///
- public static IDispatcher Instance { get; } = new NullDispatcher();
-
- ///
- public void Dispatch(Action action)
- {
- action();
- }
-
- ///
- public Task Enqueue(Action action)
- {
- action();
- return Task.CompletedTask;
- }
-
- ///
- public Task Enqueue(Func action)
- {
- return Task.FromResult(action());
- }
-
- ///
- public async Task Enqueue(Func action)
- {
- await action();
- }
-
- ///
- public async Task Enqueue(Func> action)
- {
- return await action();
- }
-
- public Task Enqueue(Func action, CancellationToken cancellationToken)
- {
- return Task.FromResult(action());
- }
-
- public Task Enqueue(Action action, CancellationToken cancellationToken)
- {
- action();
- return Task.CompletedTask;
- }
-
- public async Task Enqueue(Func action, CancellationToken cancellationToken)
- {
- await action();
- }
-
- public async Task Enqueue(Func> action, CancellationToken cancellationToken)
- {
- return await action();
- }
-
- ///
- public IDispatcher Clone() => Instance;
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/DispatcherExtension.cs b/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/DispatcherExtension.cs
deleted file mode 100644
index ef29912..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/DispatcherExtension.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net.Http;
-
-namespace ComposableAsync
-{
- ///
- /// extension methods provider
- ///
- public static class DispatcherExtension
- {
- ///
- /// Returns awaitable to enter in the dispatcher context
- /// This extension method make a dispatcher awaitable
- ///
- ///
- ///
- public static DispatcherAwaiter GetAwaiter(this IDispatcher dispatcher)
- {
- return new DispatcherAwaiter(dispatcher);
- }
-
- ///
- /// Returns a composed dispatcher applying the given dispatcher
- /// after the first one
- ///
- ///
- ///
- ///
- 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);
- }
-
- ///
- /// Returns a composed dispatcher applying the given dispatchers sequentially
- ///
- ///
- ///
- ///
- public static IDispatcher Then(this IDispatcher dispatcher, params IDispatcher[] others)
- {
- return dispatcher.Then((IEnumerable)others);
- }
-
- ///
- /// Returns a composed dispatcher applying the given dispatchers sequentially
- ///
- ///
- ///
- ///
- public static IDispatcher Then(this IDispatcher dispatcher, IEnumerable 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));
- }
-
- ///
- /// Create a from an
- ///
- ///
- ///
- public static DelegatingHandler AsDelegatingHandler(this IDispatcher dispatcher)
- {
- return new DispatcherDelegatingHandler(dispatcher);
- }
-
- ///
- /// Create a from an
- ///
- ///
- ///
- public static IDispatcher ToFullDispatcher(this IBasicDispatcher @basicDispatcher)
- {
- return new DispatcherAdapter(@basicDispatcher);
- }
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/DispatcherManager/IDispatcherManager.cs b/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/DispatcherManager/IDispatcherManager.cs
deleted file mode 100644
index 66c52d5..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/DispatcherManager/IDispatcherManager.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace ComposableAsync
-{
- ///
- /// Dispatcher manager
- ///
- public interface IDispatcherManager : IAsyncDisposable
- {
- ///
- /// true if the Dispatcher should be released
- ///
- bool DisposeDispatcher { get; }
-
- ///
- /// Returns a consumable Dispatcher
- ///
- ///
- IDispatcher GetDispatcher();
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/DispatcherManager/MonoDispatcherManager.cs b/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/DispatcherManager/MonoDispatcherManager.cs
deleted file mode 100644
index f26ef5c..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/DispatcherManager/MonoDispatcherManager.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Threading.Tasks;
-
-namespace ComposableAsync
-{
- ///
- /// implementation based on single
- ///
- public sealed class MonoDispatcherManager : IDispatcherManager
- {
- ///
- public bool DisposeDispatcher { get; }
-
- ///
- public IDispatcher GetDispatcher() => _Dispatcher;
-
- private readonly IDispatcher _Dispatcher;
-
- ///
- /// Create
- ///
- ///
- ///
- public MonoDispatcherManager(IDispatcher dispatcher, bool shouldDispose = false)
- {
- _Dispatcher = dispatcher;
- DisposeDispatcher = shouldDispose;
- }
-
- ///
- public Task DisposeAsync()
- {
- return DisposeDispatcher && (_Dispatcher is IAsyncDisposable disposable) ?
- disposable.DisposeAsync() : Task.CompletedTask;
- }
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/DispatcherProviderExtension.cs b/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/DispatcherProviderExtension.cs
deleted file mode 100644
index f7046a1..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/DispatcherProviderExtension.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace ComposableAsync
-{
- ///
- /// extension
- ///
- public static class DispatcherProviderExtension
- {
- ///
- /// Returns the underlying
- ///
- ///
- ///
- public static IDispatcher GetAssociatedDispatcher(this IDispatcherProvider dispatcherProvider)
- {
- return dispatcherProvider?.Dispatcher ?? NullDispatcher.Instance;
- }
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Disposable/ComposableAsyncDisposable.cs b/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Disposable/ComposableAsyncDisposable.cs
deleted file mode 100644
index 541d906..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Disposable/ComposableAsyncDisposable.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System.Collections.Concurrent;
-using System.Linq;
-using System.Threading.Tasks;
-
-namespace ComposableAsync
-{
- ///
- /// implementation aggregating other
- ///
- public sealed class ComposableAsyncDisposable : IAsyncDisposable
- {
- private readonly ConcurrentQueue _Disposables;
-
- ///
- /// Build an empty ComposableAsyncDisposable
- ///
- public ComposableAsyncDisposable()
- {
- _Disposables = new ConcurrentQueue();
- }
-
- ///
- /// Add an to the ComposableAsyncDisposable
- /// and returns it
- ///
- ///
- ///
- ///
- public T Add(T disposable) where T: IAsyncDisposable
- {
- if (disposable == null)
- return default(T);
-
- _Disposables.Enqueue(disposable);
- return disposable;
- }
-
- ///
- /// Dispose all the resources asynchronously
- ///
- ///
- public Task DisposeAsync()
- {
- var tasks = _Disposables.ToArray().Select(disposable => disposable.DisposeAsync()).ToArray();
- return Task.WhenAll(tasks);
- }
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Disposable/IAsyncDisposable.cs b/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Disposable/IAsyncDisposable.cs
deleted file mode 100644
index 3517491..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/Disposable/IAsyncDisposable.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System.Threading.Tasks;
-
-namespace ComposableAsync
-{
- ///
- /// Asynchronous version of IDisposable
- /// For reference see discussion: https://github.com/dotnet/roslyn/issues/114
- ///
- public interface IAsyncDisposable
- {
- ///
- /// Performs asynchronously application-defined tasks associated with freeing,
- /// releasing, or resetting unmanaged resources.
- ///
- Task DisposeAsync();
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/IBasicDispatcher.cs b/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/IBasicDispatcher.cs
deleted file mode 100644
index 6e054dd..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/IBasicDispatcher.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace ComposableAsync
-{
- ///
- /// Simplified version of that can be converted
- /// to a using the ToFullDispatcher extension method
- ///
- public interface IBasicDispatcher
- {
- ///
- /// Clone dispatcher
- ///
- ///
- IBasicDispatcher Clone();
-
- ///
- /// Enqueue the function and return a task corresponding
- /// to the execution of the task
- /// ///
- ///
- ///
- ///
- ///
- Task Enqueue(Func action, CancellationToken cancellationToken);
-
- ///
- /// Enqueue the action and return a task corresponding
- /// to the execution of the task
- ///
- ///
- ///
- ///
- Task Enqueue(Action action, CancellationToken cancellationToken);
-
- ///
- /// Enqueue the task and return a task corresponding
- /// to the execution of the task
- ///
- ///
- ///
- ///
- Task Enqueue(Func action, CancellationToken cancellationToken);
-
- ///
- /// Enqueue the task and return a task corresponding
- /// to the execution of the original task
- ///
- ///
- ///
- ///
- ///
- Task Enqueue(Func> action, CancellationToken cancellationToken);
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/IDispatcher.cs b/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/IDispatcher.cs
deleted file mode 100644
index d7eb14f..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/IDispatcher.cs
+++ /dev/null
@@ -1,98 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace ComposableAsync
-{
- ///
- /// Dispatcher executes an action or a function
- /// on its own context
- ///
- public interface IDispatcher
- {
- ///
- /// Execute action on dispatcher context in a
- /// none-blocking way
- ///
- ///
- void Dispatch(Action action);
-
- ///
- /// Enqueue the action and return a task corresponding to
- /// the completion of the action
- ///
- ///
- ///
- Task Enqueue(Action action);
-
- ///
- /// Enqueue the function and return a task corresponding to
- /// the result of the function
- ///
- ///
- ///
- ///
- Task Enqueue(Func action);
-
- ///
- /// Enqueue the task and return a task corresponding to
- /// the completion of the task
- ///
- ///
- ///
- Task Enqueue(Func action);
-
- ///
- /// Enqueue the task and return a task corresponding
- /// to the execution of the original task
- ///
- ///
- ///
- ///
- Task Enqueue(Func> action);
-
- ///
- /// Enqueue the function and return a task corresponding
- /// to the execution of the task
- /// ///
- ///
- ///
- ///
- ///
- Task Enqueue(Func action, CancellationToken cancellationToken);
-
- ///
- /// Enqueue the action and return a task corresponding
- /// to the execution of the task
- ///
- ///
- ///
- ///
- Task Enqueue(Action action, CancellationToken cancellationToken);
-
- ///
- /// Enqueue the task and return a task corresponding
- /// to the execution of the task
- ///
- ///
- ///
- ///
- Task Enqueue(Func action, CancellationToken cancellationToken);
-
- ///
- /// Enqueue the task and return a task corresponding
- /// to the execution of the original task
- ///
- ///
- ///
- ///
- ///
- Task Enqueue(Func> action, CancellationToken cancellationToken);
-
- ///
- /// Clone dispatcher
- ///
- ///
- IDispatcher Clone();
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/IDispatcherProvider.cs b/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/IDispatcherProvider.cs
deleted file mode 100644
index fb4c760..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/ComposableAsync.Core/IDispatcherProvider.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace ComposableAsync
-{
- ///
- /// Returns the fiber associated with an actor
- ///
- public interface IDispatcherProvider
- {
- ///
- /// Returns the corresponding
- ///
- IDispatcher Dispatcher { get; }
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/AwaitableConstraintExtension.cs b/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/AwaitableConstraintExtension.cs
deleted file mode 100644
index 0a2dd65..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/AwaitableConstraintExtension.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace RateLimiter
-{
- ///
- /// Provides extension to interface
- ///
- public static class AwaitableConstraintExtension
- {
- ///
- /// Compose two awaitable constraint in a new one
- ///
- ///
- ///
- ///
- public static IAwaitableConstraint Compose(this IAwaitableConstraint awaitableConstraint1, IAwaitableConstraint awaitableConstraint2)
- {
- if (awaitableConstraint1 == awaitableConstraint2)
- return awaitableConstraint1;
-
- return new ComposedAwaitableConstraint(awaitableConstraint1, awaitableConstraint2);
- }
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/ComposedAwaitableConstraint.cs b/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/ComposedAwaitableConstraint.cs
deleted file mode 100644
index 278de53..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/ComposedAwaitableConstraint.cs
+++ /dev/null
@@ -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 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();
- });
- }
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/CountByIntervalAwaitableConstraint.cs b/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/CountByIntervalAwaitableConstraint.cs
deleted file mode 100644
index 460be4f..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/CountByIntervalAwaitableConstraint.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Provide an awaitable constraint based on number of times per duration
- ///
- public class CountByIntervalAwaitableConstraint : IAwaitableConstraint
- {
- ///
- /// List of the last time stamps
- ///
- public IReadOnlyList TimeStamps => _TimeStamps.ToList();
-
- ///
- /// Stack of the last time stamps
- ///
- protected LimitedSizeStack _TimeStamps { get; }
-
- private int _Count { get; }
- private TimeSpan _TimeSpan { get; }
- private SemaphoreSlim _Semaphore { get; } = new SemaphoreSlim(1, 1);
- private ITime _Time { get; }
-
- ///
- /// Constructs a new AwaitableConstraint based on number of times per duration
- ///
- ///
- ///
- 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(_Count);
- _Time = time;
- }
-
- ///
- /// returns a task that will complete once the constraint is fulfilled
- ///
- ///
- /// Cancel the wait
- ///
- ///
- /// A disposable that should be disposed upon task completion
- ///
- public async Task WaitForReadiness(CancellationToken cancellationToken)
- {
- await _Semaphore.WaitAsync(cancellationToken);
- var count = 0;
- var now = _Time.GetNow();
- var target = now - _TimeSpan;
- LinkedListNode 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);
- }
-
- ///
- /// Clone CountByIntervalAwaitableConstraint
- ///
- ///
- public IAwaitableConstraint Clone()
- {
- return new CountByIntervalAwaitableConstraint(_Count, _TimeSpan, _Time);
- }
-
- private void OnEnded()
- {
- var now = _Time.GetNow();
- _TimeStamps.Push(now);
- OnEnded(now);
- _Semaphore.Release();
- }
-
- ///
- /// Called when action has been executed
- ///
- ///
- protected virtual void OnEnded(DateTime now)
- {
- }
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/DisposeAction.cs b/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/DisposeAction.cs
deleted file mode 100644
index 8fd44c0..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/DisposeAction.cs
+++ /dev/null
@@ -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;
- }
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/IAwaitableConstraint.cs b/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/IAwaitableConstraint.cs
deleted file mode 100644
index a9d13d3..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/IAwaitableConstraint.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace RateLimiter
-{
- ///
- /// Represents a time constraints that can be awaited
- ///
- public interface IAwaitableConstraint
- {
- ///
- /// returns a task that will complete once the constraint is fulfilled
- ///
- ///
- /// Cancel the wait
- ///
- ///
- /// A disposable that should be disposed upon task completion
- ///
- Task WaitForReadiness(CancellationToken cancellationToken);
-
- ///
- /// Returns a new IAwaitableConstraint with same constraints but unused
- ///
- ///
- IAwaitableConstraint Clone();
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/ITime.cs b/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/ITime.cs
deleted file mode 100644
index 0faa0cd..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/ITime.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace RateLimiter
-{
- ///
- /// Time abstraction
- ///
- internal interface ITime
- {
- ///
- /// Return Now DateTime
- ///
- ///
- DateTime GetNow();
-
- ///
- /// Returns a task delay
- ///
- ///
- ///
- ///
- Task GetDelay(TimeSpan timespan, CancellationToken cancellationToken);
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/LimitedSizeStack.cs b/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/LimitedSizeStack.cs
deleted file mode 100644
index bf1fe86..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/LimitedSizeStack.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System.Collections.Generic;
-
-namespace RateLimiter
-{
- ///
- /// LinkedList with a limited size
- /// If the size exceeds the limit older entry are removed
- ///
- ///
- public class LimitedSizeStack: LinkedList
- {
- private readonly int _MaxSize;
-
- ///
- /// Construct the LimitedSizeStack with the given limit
- ///
- ///
- public LimitedSizeStack(int maxSize)
- {
- _MaxSize = maxSize;
- }
-
- ///
- /// Push new entry. If he size exceeds the limit, the oldest entry is removed
- ///
- ///
- public void Push(T item)
- {
- AddFirst(item);
-
- if (Count > _MaxSize)
- RemoveLast();
- }
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/PersistentCountByIntervalAwaitableConstraint.cs b/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/PersistentCountByIntervalAwaitableConstraint.cs
deleted file mode 100644
index cda1e92..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/PersistentCountByIntervalAwaitableConstraint.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace RateLimiter
-{
- ///
- /// that is able to save own state.
- ///
- public sealed class PersistentCountByIntervalAwaitableConstraint : CountByIntervalAwaitableConstraint
- {
- private readonly Action _SaveStateAction;
-
- ///
- /// Create an instance of .
- ///
- /// Maximum actions allowed per time interval.
- /// Time interval limits are applied for.
- /// Action is used to save state.
- /// Initial timestamps.
- public PersistentCountByIntervalAwaitableConstraint(int count, TimeSpan timeSpan,
- Action saveStateAction, IEnumerable initialTimeStamps) : base(count, timeSpan)
- {
- _SaveStateAction = saveStateAction;
-
- if (initialTimeStamps == null)
- return;
-
- foreach (var timeStamp in initialTimeStamps)
- {
- _TimeStamps.Push(timeStamp);
- }
- }
-
- ///
- /// Save state
- ///
- protected override void OnEnded(DateTime now)
- {
- _SaveStateAction(now);
- }
- }
-}
\ No newline at end of file
diff --git a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/TimeLimiter.cs b/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/TimeLimiter.cs
deleted file mode 100644
index e36b8e9..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/TimeLimiter.cs
+++ /dev/null
@@ -1,206 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using ComposableAsync;
-
-namespace RateLimiter
-{
- ///
- /// TimeLimiter implementation
- ///
- public class TimeLimiter : IDispatcher
- {
- private readonly IAwaitableConstraint _AwaitableConstraint;
-
- internal TimeLimiter(IAwaitableConstraint awaitableConstraint)
- {
- _AwaitableConstraint = awaitableConstraint;
- }
-
- ///
- /// Perform the given task respecting the time constraint
- /// returning the result of given function
- ///
- ///
- ///
- public Task Enqueue(Func perform)
- {
- return Enqueue(perform, CancellationToken.None);
- }
-
- ///
- /// Perform the given task respecting the time constraint
- /// returning the result of given function
- ///
- ///
- ///
- ///
- public Task Enqueue(Func> perform)
- {
- return Enqueue(perform, CancellationToken.None);
- }
-
- ///
- /// Perform the given task respecting the time constraint
- ///
- ///
- ///
- ///
- public async Task Enqueue(Func perform, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
- using (await _AwaitableConstraint.WaitForReadiness(cancellationToken))
- {
- await perform();
- }
- }
-
- ///
- /// Perform the given task respecting the time constraint
- /// returning the result of given function
- ///
- ///
- ///
- ///
- ///
- public async Task Enqueue(Func> perform, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
- using (await _AwaitableConstraint.WaitForReadiness(cancellationToken))
- {
- return await perform();
- }
- }
-
- public IDispatcher Clone() => new TimeLimiter(_AwaitableConstraint.Clone());
-
- private static Func Transform(Action act)
- {
- return () => { act(); return Task.FromResult(0); };
- }
-
- ///
- /// Perform the given task respecting the time constraint
- /// returning the result of given function
- ///
- ///
- ///
- ///
- private static Func> Transform(Func compute)
- {
- return () => Task.FromResult(compute());
- }
-
- ///
- /// Perform the given task respecting the time constraint
- ///
- ///
- ///
- public Task Enqueue(Action perform)
- {
- var transformed = Transform(perform);
- return Enqueue(transformed);
- }
-
- ///
- /// Perform the given task respecting the time constraint
- ///
- ///
- public void Dispatch(Action action)
- {
- Enqueue(action);
- }
-
- ///
- /// Perform the given task respecting the time constraint
- /// returning the result of given function
- ///
- ///
- ///
- ///
- public Task Enqueue(Func perform)
- {
- var transformed = Transform(perform);
- return Enqueue(transformed);
- }
-
- ///
- /// Perform the given task respecting the time constraint
- /// returning the result of given function
- ///
- ///
- ///
- ///
- ///
- public Task Enqueue(Func perform, CancellationToken cancellationToken)
- {
- var transformed = Transform(perform);
- return Enqueue(transformed, cancellationToken);
- }
-
- ///
- /// Perform the given task respecting the time constraint
- ///
- ///
- ///
- ///
- public Task Enqueue(Action perform, CancellationToken cancellationToken)
- {
- var transformed = Transform(perform);
- return Enqueue(transformed, cancellationToken);
- }
-
- ///
- /// Returns a TimeLimiter based on a maximum number of times
- /// during a given period
- ///
- ///
- ///
- ///
- public static TimeLimiter GetFromMaxCountByInterval(int maxCount, TimeSpan timeSpan)
- {
- return new TimeLimiter(new CountByIntervalAwaitableConstraint(maxCount, timeSpan));
- }
-
- ///
- /// Create that will save state using action passed through parameter.
- ///
- /// Maximum actions allowed per time interval.
- /// Time interval limits are applied for.
- /// Action is used to save state.
- /// instance with .
- public static TimeLimiter GetPersistentTimeLimiter(int maxCount, TimeSpan timeSpan,
- Action saveStateAction)
- {
- return GetPersistentTimeLimiter(maxCount, timeSpan, saveStateAction, null);
- }
-
- ///
- /// Create with initial timestamps that will save state using action passed through parameter.
- ///
- /// Maximum actions allowed per time interval.
- /// Time interval limits are applied for.
- /// Action is used to save state.
- /// Initial timestamps.
- /// instance with .
- public static TimeLimiter GetPersistentTimeLimiter(int maxCount, TimeSpan timeSpan,
- Action saveStateAction, IEnumerable initialTimeStamps)
- {
- return new TimeLimiter(new PersistentCountByIntervalAwaitableConstraint(maxCount, timeSpan, saveStateAction, initialTimeStamps));
- }
-
- ///
- /// Compose various IAwaitableConstraint in a TimeLimiter
- ///
- ///
- ///
- 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);
- }
- }
-}
diff --git a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/TimeSystem.cs b/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/TimeSystem.cs
deleted file mode 100644
index 81b46e1..0000000
--- a/Jellyfin.Plugin.Danmu/Vendor/RateLimiter/TimeSystem.cs
+++ /dev/null
@@ -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);
- }
- }
-}
diff --git a/README.md b/README.md
index e00d524..676f6db 100644
--- a/README.md
+++ b/README.md
@@ -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