From 67446a7c3f5ef2771844a0671ccae3e80a0860ad Mon Sep 17 00:00:00 2001 From: Ronny Date: Sat, 17 Aug 2024 13:08:01 +0200 Subject: [PATCH] introduce fluent observable and per minute rate limit --- web/src/engine/Observable.ts | 6 ++++-- web/src/engine/taskpool/RateLimit.ts | 8 ++++++++ web/src/engine/websites/MangaDex.ts | 5 ----- web/src/engine/websites/ReaperScans.ts | 5 ++--- web/src/engine/websites/TruyenQQ.ts | 6 ++---- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/web/src/engine/Observable.ts b/web/src/engine/Observable.ts index e1ccf37d7..7d9399bd0 100644 --- a/web/src/engine/Observable.ts +++ b/web/src/engine/Observable.ts @@ -51,15 +51,17 @@ export class Observable implements IObservable): void { + public Subscribe(callback: SubscriberCallback): typeof this { this.subscribers.add(callback); + return this; } /** * Unregister the {@link callback} from any further notifications. */ - public Unsubscribe(callback: SubscriberCallback): void { + public Unsubscribe(callback: SubscriberCallback): typeof this { this.subscribers.delete(callback); + return this; } } diff --git a/web/src/engine/taskpool/RateLimit.ts b/web/src/engine/taskpool/RateLimit.ts index 75bd64512..9d33b300c 100644 --- a/web/src/engine/taskpool/RateLimit.ts +++ b/web/src/engine/taskpool/RateLimit.ts @@ -15,6 +15,14 @@ export class RateLimit { public get Throttle() { return this.amount > 0 && this.interval > 0 ? this.interval * 1000 / this.amount : 0; } + + /** + * Create a new {@link RateLimit} instance. + * @param amount - Maximum number of units within an interval of 60 seconds. + */ + public static PerMinute(amount: number) { + return new RateLimit(amount, 60); + } } export const Unlimited = new RateLimit(0, 0); \ No newline at end of file diff --git a/web/src/engine/websites/MangaDex.ts b/web/src/engine/websites/MangaDex.ts index cf29450ef..5d1f92b75 100755 --- a/web/src/engine/websites/MangaDex.ts +++ b/web/src/engine/websites/MangaDex.ts @@ -5,8 +5,6 @@ import { MangaScraper, type MangaPlugin, Manga, Chapter, Page } from '../provide import { TaskPool, Priority } from '../taskpool/TaskPool'; import { RateLimit } from '../taskpool/RateLimit'; import * as Common from './decorators/Common'; -import { Numeric } from '../SettingsManager'; -import { WebsiteResourceKey as R } from '../../i18n/ILocale'; type CachedManga = { id: string, @@ -101,13 +99,10 @@ const chapterLanguageMap = new Map([ export default class extends MangaScraper { private readonly api = 'https://api.mangadex.org'; - private readonly mangasTaskPool = new TaskPool(1, new RateLimit(2, 1)); private readonly chaptersTaskPool = new TaskPool(1, new RateLimit(4, 1)); public constructor() { super('mangadex', 'MangaDex', 'https://mangadex.org', Tags.Media.Manga, Tags.Media.Manhwa, Tags.Media.Manhua, Tags.Language.Multilingual, Tags.Source.Aggregator, Tags.Source.Scanlator); - this.Settings.throttle = new Numeric('throttle.mangas', R.Plugin_Settings_ThrottlingInteraction, R.Plugin_Settings_ThrottlingInteractionInfo, 60, 6, 240); - (this.Settings.throttle as Numeric).Subscribe(value => this.mangasTaskPool.RateLimit = new RateLimit(value, 60)); } public override get Icon() { diff --git a/web/src/engine/websites/ReaperScans.ts b/web/src/engine/websites/ReaperScans.ts index d7376318f..428b5c699 100755 --- a/web/src/engine/websites/ReaperScans.ts +++ b/web/src/engine/websites/ReaperScans.ts @@ -36,12 +36,11 @@ type LaravelLivewireMessage = { @Common.ImageAjax() export default class extends DecoratableMangaScraper { - private readonly interactionTaskPool = new TaskPool(1, new RateLimit(15, 60)); + private readonly interactionTaskPool = new TaskPool(1, RateLimit.PerMinute(15)); public constructor() { super('reaperscans', 'Reaper Scans', 'https://reaperscans.com', Tags.Media.Manhwa, Tags.Media.Manhua, Tags.Language.English); - this.Settings.throttle = new Numeric('throttle.interactive', R.Plugin_Settings_ThrottlingInteraction, R.Plugin_Settings_ThrottlingInteractionInfo, 15, 1, 60); - (this.Settings.throttle as Numeric).Subscribe(value => this.interactionTaskPool.RateLimit = new RateLimit(value, 60)); + this.Settings.throttle = new Numeric('throttle.interactive', R.Plugin_Settings_ThrottlingInteraction, R.Plugin_Settings_ThrottlingInteractionInfo, 15, 1, 60).Subscribe(value => this.interactionTaskPool.RateLimit = RateLimit.PerMinute(value)); } public override get Icon() { diff --git a/web/src/engine/websites/TruyenQQ.ts b/web/src/engine/websites/TruyenQQ.ts index b43e8eb15..ed77e2555 100644 --- a/web/src/engine/websites/TruyenQQ.ts +++ b/web/src/engine/websites/TruyenQQ.ts @@ -21,13 +21,11 @@ function PageExtractor(element: HTMLElement): string { @Common.ImageAjax() export default class extends DecoratableMangaScraper { - private readonly interactionTaskPool = new TaskPool(1, new RateLimit(30, 60)); + private readonly interactionTaskPool = new TaskPool(1, RateLimit.PerMinute(30)); public constructor() { super('truyenqq', 'TruyenQQ', 'https://truyenqqviet.com', Tags.Media.Manhwa, Tags.Media.Manhua, Tags.Language.Vietnamese, Tags.Source.Aggregator); - const throttle = new Numeric('throttle.interactive', R.Plugin_Settings_ThrottlingInteraction, R.Plugin_Settings_ThrottlingInteractionInfo, 30, 1, 60); - throttle.Subscribe(value => this.interactionTaskPool.RateLimit = new RateLimit(value, 60)); - this.Settings.throttle = throttle; + this.Settings.throttle = new Numeric('throttle.interactive', R.Plugin_Settings_ThrottlingInteraction, R.Plugin_Settings_ThrottlingInteractionInfo, 30, 1, 60).Subscribe(value => this.interactionTaskPool.RateLimit = RateLimit.PerMinute(value)); } public override get Icon() {