From ef0dbef9cd31c173054f0d830d731c911dd40644 Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Tue, 30 Jun 2026 14:58:22 +0200 Subject: [PATCH] Use new "binTrackRecord" mutation during migration Does not cause any requests to the trackers during the migration, which prevents getting rate limited closes #1104 --- src/features/migration/MangaMigration.ts | 36 +------------------ src/lib/graphql/generated/apollo-helpers.ts | 15 ++++++++ .../graphql/generated/graphql-base.types.ts | 17 +++++++++ src/lib/graphql/generated/graphql.ts | 36 +++++++++++++++++++ src/lib/graphql/tracker/TrackerMutation.ts | 22 ++++++++++++ src/lib/requests/RequestManager.ts | 16 +++++++++ 6 files changed, 107 insertions(+), 35 deletions(-) diff --git a/src/features/migration/MangaMigration.ts b/src/features/migration/MangaMigration.ts index 3f07e50d..07645ef0 100644 --- a/src/features/migration/MangaMigration.ts +++ b/src/features/migration/MangaMigration.ts @@ -21,27 +21,10 @@ import type { MigrationChapter, } from '@/features/migration/Migration.types.ts'; import { getMetadataServerSettings } from '@/features/settings/services/ServerSettingsMetadata.ts'; -import { Queue } from '@/lib/Queue.ts'; -import type { TrackerIdInfo } from '@/features/tracker/Tracker.types.ts'; -import { assertIsDefined } from '@/base/Asserts.ts'; import { t } from '@lingui/core/macro'; import { makeToast } from '@/base/utils/Toast.ts'; export class MangaMigration { - private static trackerQueue = new Map(); - - private static getOrCreateQueue(trackerId: TrackerIdInfo['id']): Queue { - if (!MangaMigration.trackerQueue.has(trackerId)) { - MangaMigration.trackerQueue.set(trackerId, new Queue(1)); - } - - const queue = MangaMigration.trackerQueue.get(trackerId); - - assertIsDefined(queue); - - return queue; - } - static async migrate( mangaToMigrate: MangaToMigrate | null | undefined, mangaToMigrateTo: MangaToMigrateTo | null | undefined, @@ -285,24 +268,7 @@ export class MangaMigration { return { copy: () => trackBindingsToAdd.map( - (trackRecord) => - MangaMigration.getOrCreateQueue(trackRecord.trackerId).enqueue( - String(mangaToMigrate.id), - async () => { - try { - await requestManager.bindTracker( - mangaToMigrateTo.id, - trackRecord.trackerId, - trackRecord.remoteId, - trackRecord.private, - ).response; - } finally { - await new Promise((resolve) => { - setTimeout(resolve, 500); - }); - } - }, - ).promise, + (trackRecord) => requestManager.bindTrackRecord(mangaToMigrateTo.id, trackRecord.id).response, ), cleanup: () => mode === 'migrate' diff --git a/src/lib/graphql/generated/apollo-helpers.ts b/src/lib/graphql/generated/apollo-helpers.ts index a92280dc..84fa6081 100644 --- a/src/lib/graphql/generated/apollo-helpers.ts +++ b/src/lib/graphql/generated/apollo-helpers.ts @@ -40,6 +40,15 @@ export type BindTrackPayloadFieldPolicy = { clientMutationId?: FieldPolicy | FieldReadFunction; trackRecord?: FieldPolicy | FieldReadFunction; }; +export type BindTrackRecordPayloadKeySpecifier = ( + | 'clientMutationId' + | 'trackRecord' + | BindTrackRecordPayloadKeySpecifier +)[]; +export type BindTrackRecordPayloadFieldPolicy = { + clientMutationId?: FieldPolicy | FieldReadFunction; + trackRecord?: FieldPolicy | FieldReadFunction; +}; export type CategoryEdgeKeySpecifier = ('cursor' | 'node' | CategoryEdgeKeySpecifier)[]; export type CategoryEdgeFieldPolicy = { cursor?: FieldPolicy | FieldReadFunction; @@ -869,6 +878,7 @@ export type MultiSelectListPreferenceFieldPolicy = { }; export type MutationKeySpecifier = ( | 'bindTrack' + | 'bindTrackRecord' | 'clearCachedImages' | 'clearDownloader' | 'connectKoSyncAccount' @@ -948,6 +958,7 @@ export type MutationKeySpecifier = ( )[]; export type MutationFieldPolicy = { bindTrack?: FieldPolicy | FieldReadFunction; + bindTrackRecord?: FieldPolicy | FieldReadFunction; clearCachedImages?: FieldPolicy | FieldReadFunction; clearDownloader?: FieldPolicy | FieldReadFunction; connectKoSyncAccount?: FieldPolicy | FieldReadFunction; @@ -2455,6 +2466,10 @@ export type StrictTypedTypePolicies = { keyFields?: false | BindTrackPayloadKeySpecifier | (() => undefined | BindTrackPayloadKeySpecifier); fields?: BindTrackPayloadFieldPolicy; }; + BindTrackRecordPayload?: Omit & { + keyFields?: false | BindTrackRecordPayloadKeySpecifier | (() => undefined | BindTrackRecordPayloadKeySpecifier); + fields?: BindTrackRecordPayloadFieldPolicy; + }; CategoryEdge?: Omit & { keyFields?: false | CategoryEdgeKeySpecifier | (() => undefined | CategoryEdgeKeySpecifier); fields?: CategoryEdgeFieldPolicy; diff --git a/src/lib/graphql/generated/graphql-base.types.ts b/src/lib/graphql/generated/graphql-base.types.ts index f231d71a..68784119 100644 --- a/src/lib/graphql/generated/graphql-base.types.ts +++ b/src/lib/graphql/generated/graphql-base.types.ts @@ -71,6 +71,18 @@ export type BindTrackPayload = { trackRecord: TrackRecordType; }; +export type BindTrackRecordInput = { + clientMutationId?: InputMaybe; + mangaId: Scalars['Int']['input']; + trackRecordId: Scalars['Int']['input']; +}; + +export type BindTrackRecordPayload = { + __typename?: 'BindTrackRecordPayload'; + clientMutationId?: Maybe; + trackRecord: TrackRecordType; +}; + export type BooleanFilterInput = { distinctFrom?: InputMaybe; distinctFromAll?: InputMaybe>; @@ -1310,6 +1322,7 @@ export type MultiSelectListPreference = { export type Mutation = { __typename?: 'Mutation'; bindTrack: BindTrackPayload; + bindTrackRecord?: Maybe; clearCachedImages: ClearCachedImagesPayload; clearDownloader?: Maybe; connectKoSyncAccount: KoSyncConnectPayload; @@ -1391,6 +1404,10 @@ export type MutationBindTrackArgs = { input: BindTrackInput; }; +export type MutationBindTrackRecordArgs = { + input: BindTrackRecordInput; +}; + export type MutationClearCachedImagesArgs = { input: ClearCachedImagesInput; }; diff --git a/src/lib/graphql/generated/graphql.ts b/src/lib/graphql/generated/graphql.ts index b037bcd1..ad4934fd 100644 --- a/src/lib/graphql/generated/graphql.ts +++ b/src/lib/graphql/generated/graphql.ts @@ -3951,6 +3951,42 @@ export type TrackerBindMutation = { }; }; +export type TrackerBindTrackRecordMutationVariables = Exact<{ + input: Types.BindTrackRecordInput; +}>; + +export type TrackerBindTrackRecordMutation = { + __typename: 'Mutation'; + bindTrackRecord: { + __typename: 'BindTrackRecordPayload'; + trackRecord: { + __typename: 'TrackRecordType'; + id: number; + remoteId: string; + trackerId: number; + remoteUrl: string; + title: string; + status: number; + lastChapterRead: number; + totalChapters: number; + score: number; + displayScore: string; + startDate: string; + finishDate: string; + private: boolean; + manga: { + __typename: 'MangaType'; + id: number; + trackRecords: { + __typename: 'TrackRecordNodeList'; + totalCount: number; + nodes: Array<{ __typename: 'TrackRecordType'; id: number; trackerId: number }>; + }; + }; + }; + } | null; +}; + export type TrackerUnbindMutationVariables = Exact<{ input: Types.UnbindTrackInput; }>; diff --git a/src/lib/graphql/tracker/TrackerMutation.ts b/src/lib/graphql/tracker/TrackerMutation.ts index a5d1df66..ead382c2 100644 --- a/src/lib/graphql/tracker/TrackerMutation.ts +++ b/src/lib/graphql/tracker/TrackerMutation.ts @@ -69,6 +69,28 @@ export const TRACKER_BIND = gql` } `; +export const TRACK_BIND_TRACK_RECORD = gql` + ${TRACK_RECORD_BIND_FIELDS} + + mutation TRACKER_BIND_TRACK_RECORD($input: BindTrackRecordInput!) { + bindTrackRecord(input: $input) { + trackRecord { + ...TRACK_RECORD_BIND_FIELDS + manga { + id + trackRecords { + totalCount + nodes { + id + trackerId + } + } + } + } + } + } +`; + export const TRACKER_UNBIND = gql` mutation TRACKER_UNBIND($input: UnbindTrackInput!) { unbindTrack(input: $input) { diff --git a/src/lib/requests/RequestManager.ts b/src/lib/requests/RequestManager.ts index 3f42faf3..bf6d4d4f 100644 --- a/src/lib/requests/RequestManager.ts +++ b/src/lib/requests/RequestManager.ts @@ -126,6 +126,8 @@ import type { StopUpdaterMutationVariables, TrackerBindMutation, TrackerBindMutationVariables, + TrackerBindTrackRecordMutation, + TrackerBindTrackRecordMutationVariables, TrackerFetchBindMutation, TrackerFetchBindMutationVariables, TrackerLoginCredentialsMutation, @@ -309,6 +311,7 @@ import type { QueuePriority } from '@/lib/Queue.ts'; import { SourceAwareQueue } from '@/lib/SourceAwareQueue.ts'; import { TRACKER_SEARCH } from '@/lib/graphql/tracker/TrackerQuery.ts'; import { + TRACK_BIND_TRACK_RECORD, TRACKER_BIND, TRACKER_FETCH_BIND, TRACKER_LOGIN_CREDENTIALS, @@ -3719,6 +3722,19 @@ export class RequestManager { ); } + public bindTrackRecord( + mangaId: number, + trackRecordId: number, + options?: MutationOptions, + ): AbortableApolloMutationResponse { + return this.doRequest( + GQLMethod.MUTATION, + TRACK_BIND_TRACK_RECORD, + { input: { mangaId, trackRecordId } }, + options, + ); + } + public unbindTracker( recordId: number, deleteRemoteTrack?: boolean,