diff --git a/.gitattributes b/.gitattributes index 4fea60b95..9df66562f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,14 @@ go.mod text eol=lf -go.sum text eol=lf \ No newline at end of file +go.sum text eol=lf + +frontend/src/graphql/types.ts linguist-generated=true +internal/converter/gen/generated.go linguist-generated=true +internal/dataloader/*_gen.go linguist-generated=true +internal/models/generated_*.go linguist-generated=true + +# some of the files in internal/queries/ are handwritten +internal/queries/*.sql.go linguist-generated=true +internal/queries/copyfrom.go linguist-generated=true +internal/queries/db.go linguist-generated=true +internal/queries/models.go linguist-generated=true +internal/queries/querier.go linguist-generated=true diff --git a/Makefile b/Makefile index 90a0214f5..f4ce450e6 100644 --- a/Makefile +++ b/Makefile @@ -77,6 +77,8 @@ generate-dataloaders: go run github.com/vektah/dataloaden SceneAppearancesLoader github.com/gofrs/uuid.UUID "[]github.com/stashapp/stash-box/internal/models.PerformerScene"; \ go run github.com/vektah/dataloaden PerformerLoader github.com/gofrs/uuid.UUID "*github.com/stashapp/stash-box/internal/models.Performer"; \ go run github.com/vektah/dataloaden ImageLoader github.com/gofrs/uuid.UUID "*github.com/stashapp/stash-box/internal/models.Image"; \ + go run github.com/vektah/dataloaden ImageTypeAssignmentsLoader github.com/gofrs/uuid.UUID "[]github.com/stashapp/stash-box/internal/models.ImageTypeAssignment"; \ + go run github.com/vektah/dataloaden ImageDatesLoader github.com/gofrs/uuid.UUID "[]github.com/stashapp/stash-box/internal/models.ImageDate"; \ go run github.com/vektah/dataloaden FingerprintsLoader github.com/gofrs/uuid.UUID "[]github.com/stashapp/stash-box/internal/models.Fingerprint"; \ go run github.com/vektah/dataloaden SubmittedFingerprintsLoader github.com/gofrs/uuid.UUID "[]github.com/stashapp/stash-box/internal/models.Fingerprint"; \ go run github.com/vektah/dataloaden BodyModificationsLoader github.com/gofrs/uuid.UUID "[]github.com/stashapp/stash-box/internal/models.BodyModification"; \ diff --git a/e2e/support/fixtures/square-png.ts b/e2e/support/fixtures/square-png.ts new file mode 100644 index 000000000..0e6311e0b --- /dev/null +++ b/e2e/support/fixtures/square-png.ts @@ -0,0 +1,74 @@ +// A square PNG for the crop e2e test, generated at load time and written to a +// stable path because Playwright's setInputFiles takes a path, not a buffer +// +// Square on purpose: every crop template is portrait, so cropping to one is a +// change the assertions can see. The 1x1 JPEG the other image tests use has no +// room to drag a frame in + +import { deflateSync } from "node:zlib"; +import { writeFileSync, existsSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +const SIZE = 900; + +const crcTable = Array.from({ length: 256 }, (_, n) => { + let c = n; + for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1; + return c >>> 0; +}); + +const crc32 = (buf: Buffer) => { + let c = 0xffffffff; + for (const byte of buf) c = crcTable[(c ^ byte) & 0xff] ^ (c >>> 8); + return (c ^ 0xffffffff) >>> 0; +}; + +const chunk = (type: string, data: Buffer) => { + const length = Buffer.alloc(4); + length.writeUInt32BE(data.length); + const body = Buffer.concat([Buffer.from(type, "ascii"), data]); + const crc = Buffer.alloc(4); + crc.writeUInt32BE(crc32(body)); + return Buffer.concat([length, body, crc]); +}; + +const png = () => { + const ihdr = Buffer.alloc(13); + ihdr.writeUInt32BE(SIZE, 0); + ihdr.writeUInt32BE(SIZE, 4); + ihdr[8] = 8; // bit depth + ihdr[9] = 0; // grayscale + // compression, filter, interlace all 0 + + // One filter byte per row, then the row itself. A gradient rather than a + // flat fill, so a crop of the wrong region is distinguishable from the right + // one if anyone ever looks at the bytes + const raw = Buffer.alloc(SIZE * (SIZE + 1)); + for (let y = 0; y < SIZE; y++) { + const row = y * (SIZE + 1); + raw[row] = 0; + for (let x = 0; x < SIZE; x++) raw[row + 1 + x] = (x + y) & 0xff; + } + + return Buffer.concat([ + Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), + chunk("IHDR", ihdr), + chunk("IDAT", deflateSync(raw)), + chunk("IEND", Buffer.alloc(0)), + ]); +}; + +let path: string | undefined; + +// Absolute path to the fixture, written on first use +export const squarePngPath = () => { + if (!path) { + path = join(tmpdir(), `stash-box-e2e-${SIZE}x${SIZE}.png`); + if (!existsSync(path)) writeFileSync(path, png()); + } + return path; +}; + +// The source's own dimensions, for asserting that a crop changed them +export const SQUARE_PNG_SIZE = SIZE; diff --git a/e2e/tests/auth/role-authorization.spec.ts b/e2e/tests/auth/role-authorization.spec.ts index 2d7f7f4b1..316e0fafe 100644 --- a/e2e/tests/auth/role-authorization.spec.ts +++ b/e2e/tests/auth/role-authorization.spec.ts @@ -8,7 +8,11 @@ import { test, expect } from "../../support/fixtures"; import { graphqlAs } from "../../support/helpers/graphql"; -import { adminApi, submitStudioCreateEdit, uniq } from "../../support/helpers/seed"; +import { + adminApi, + submitStudioCreateEdit, + uniq, +} from "../../support/helpers/seed"; type Role = "read" | "vote" | "edit" | "modify" | "moderate"; @@ -174,10 +178,7 @@ test("admin can perform all the mutations above", async () => { `mutation($input: TagCategoryCreateInput!) { tagCategoryCreate(input: $input) { id } }`, { input: { name: uniq("cat"), group: "SCENE" } }, ], - [ - `query { queryUsers(input: { page: 1, per_page: 1 }) { count } }`, - {}, - ], + [`query { queryUsers(input: { page: 1, per_page: 1 }) { count } }`, {}], ]; for (const [q, v] of mutations) { const res = await admin.post("/graphql", { @@ -189,3 +190,8 @@ test("admin can perform all the mutations above", async () => { } await admin.dispose(); }); + +test("image type order screen is admin-only", async ({ readPage }) => { + await readPage.goto("/image-types"); + await expect(readPage.getByText(/do not have permission/i)).toBeVisible(); +}); diff --git a/e2e/tests/entities/image-preferences.spec.ts b/e2e/tests/entities/image-preferences.spec.ts new file mode 100644 index 000000000..a313c8a0f --- /dev/null +++ b/e2e/tests/entities/image-preferences.spec.ts @@ -0,0 +1,97 @@ +import { test, expect } from "../../support/fixtures"; +import { graphqlAs } from "../../support/helpers/graphql"; +import { gql } from "../../support/helpers/seed"; + +// The stored key behind a display name, which is all the screen shows +const keyNamed = async (name: string) => { + const api = await graphqlAs("e2e_edit"); + const data = await gql<{ + imageTypeGroups: { types: { key: string; name: string }[] }[]; + }>(api, `query { imageTypeGroups { types { key name } } }`); + await api.dispose(); + + const match = data.imageTypeGroups + .flatMap((group) => group.types) + .find((type) => type.name === name); + if (!match) throw new Error(`no image type named ${name}`); + return match.key; +}; + +const groupPreferencesOf = async (username: string) => { + const api = await graphqlAs(username); + const data = await gql<{ me: { image_type_group_preferences: string[] } }>( + api, + `query { me { image_type_group_preferences } }`, + ); + await api.dispose(); + return data.me.image_type_group_preferences; +}; + +const preferencesOf = async (username: string) => { + const api = await graphqlAs(username); + const data = await gql<{ me: { image_type_preferences: string[] } }>( + api, + `query { me { image_type_preferences } }`, + ); + await api.dispose(); + return data.me.image_type_preferences; +}; + +test("image preference screen saves an order and clears it", async ({ + editPage, +}) => { + try { + await editPage.goto("/users/e2e_edit/image-types"); + await editPage.waitForLoadState("networkidle"); + + const firstGroup = editPage.locator(".card-body").first(); + const rows = firstGroup.locator(".DragList-row"); + const nameOf = (index: number) => + rows.nth(index).locator(".DragList-content").innerText(); + + const wasSecond = await nameOf(1); + + // Promote the second type of the first group + await rows.nth(1).locator(".DragList-handle").dragTo(rows.nth(0)); + + expect(await nameOf(0)).toBe(wasSecond); + + await editPage.getByRole("button", { name: "Save", exact: true }).click(); + await expect(editPage.getByText("Preferences saved.")).toBeVisible(); + + // We save the full ordering every time + const saved = await preferencesOf("e2e_edit"); + expect(saved.length).toBeGreaterThan(1); + expect(saved[0]).toBe(await keyNamed(wasSecond)); + + // Groups reorder the same way, and are the stronger of the two + const groupRows = editPage.locator(".DragList.is-block > .DragList-row"); + const groupNames = () => editPage.locator(".card-header b").allInnerTexts(); + const groupsBefore = await groupNames(); + + await groupRows + .nth(0) + .locator("> .DragList-handle") + .dragTo(groupRows.nth(1), { targetPosition: { x: 20, y: 12 } }); + + expect((await groupNames())[0]).toBe(groupsBefore[1]); + + await editPage.getByRole("button", { name: "Save", exact: true }).click(); + await expect(editPage.getByText("Preferences saved.")).toBeVisible(); + expect((await groupPreferencesOf("e2e_edit")).length).toBeGreaterThan(1); + + await editPage.getByRole("button", { name: "Use site default" }).click(); + await expect(editPage.getByText("Preferences saved.")).toBeVisible(); + + expect(await preferencesOf("e2e_edit")).toEqual([]); + expect(await groupPreferencesOf("e2e_edit")).toEqual([]); + } finally { + // Shared fixture user so we want them to go back to the default ordering + const api = await graphqlAs("e2e_edit"); + await gql( + api, + `mutation { updateImageTypePreferences(input: { types: [], groups: [] }) }`, + ); + await api.dispose(); + } +}); diff --git a/e2e/tests/entities/image-types.spec.ts b/e2e/tests/entities/image-types.spec.ts new file mode 100644 index 000000000..255374f13 --- /dev/null +++ b/e2e/tests/entities/image-types.spec.ts @@ -0,0 +1,173 @@ +import { test, expect } from "../../support/fixtures"; +import { + adminApi, + createPerformer, + gql, + uniq, +} from "../../support/helpers/seed"; +import { tinyJpegPath } from "../../support/fixtures/tiny-jpeg"; + +// The admin image type screen: which of the vocabulary this instance uses and +// in what order. The taxonomy itself is seeded by migration so ordering and +// switching entries off cover the entirety of an admins power + +// Everything here writes instance-wide state and puts it back afterwards, so +// these cannot overlap: the screen refetches on any change, and a concurrent +// write would replace what the test under way had just toggled on screen. +test.describe.configure({ mode: "serial" }); + +type Group = { key: string; types: { key: string }[] }; + +const readOrder = async () => { + const admin = await adminApi(); + const data = await gql<{ imageTypeGroups: Group[] }>( + admin, + `query { imageTypeGroups { key types { key } } }`, + ); + await admin.dispose(); + return data.imageTypeGroups; +}; + +// Ordering is instance-wide state, so a test that changes it puts it back +const restoreOrder = async (groups: Group[]) => { + const admin = await adminApi(); + await gql( + admin, + `mutation($input: ImageTypeOrderInput!) { + imageTypeOrderUpdate(input: $input) { key } + }`, + { + input: { + groups: groups.map((g) => g.key), + types: groups.flatMap((g) => g.types.map((t) => t.key)), + }, + }, + ); + await admin.dispose(); +}; + +test("admin reorders image type groups and types through the UI", async ({ + adminPage, +}) => { + const before = await readOrder(); + + try { + await adminPage.goto("/image-types"); + await adminPage.waitForLoadState("networkidle"); + + // Groups render in priority order; demote the first one + const groupRows = adminPage.locator(".DragList.is-block > .DragList-row"); + await groupRows + .nth(0) + .locator("> .DragList-handle") + .dragTo(groupRows.nth(1), { targetPosition: { x: 20, y: 12 } }); + + // Then demote the first type inside what is now the leading group + const typeRows = adminPage + .locator(".card-body") + .first() + .locator(".DragList-row"); + await typeRows.nth(0).locator(".DragList-handle").dragTo(typeRows.nth(1)); + + await adminPage.getByRole("button", { name: "Save Order" }).click(); + + // Saving is instance-wide so we need to confirm through the modal + await adminPage.getByRole("button", { name: "Save for everyone" }).click(); + await expect(adminPage.getByText("Order saved.")).toBeVisible(); + + const after = await readOrder(); + expect(after[0].key).toBe(before[1].key); + expect(after[1].key).toBe(before[0].key); + + // The group that moved to the front had its first two types swapped. + const moved = after.find((g) => g.key === before[1].key); + expect(moved?.types[0].key).toBe(before[1].types[1].key); + expect(moved?.types[1].key).toBe(before[1].types[0].key); + } finally { + await restoreOrder(before); + } +}); + +// Switching a type off is instance-wide too, so it is restored the same way +const restoreEnabled = async () => { + const admin = await adminApi(); + await gql( + admin, + `mutation { + imageTypeSetEnabled(input: { disabled_groups: [], disabled_types: [] }) { key } + }`, + ); + await admin.dispose(); +}; + +test("admin switches a type off and it stops being offered", async ({ + adminPage, + editPage, +}) => { + try { + await adminPage.goto("/image-types"); + await adminPage.waitForLoadState("networkidle"); + + // Whichever type leads the first group, skipping the few other specs + // label with: switching one off is instance-wide and takes effect at once, + // so disabling one of those would fail a spec running alongside this one. + // Chosen by position rather than named, since the taxonomy is reorderable. + const inUseElsewhere = ["Portrait", "Face", "Candid"]; + const names = await adminPage + .locator(".card-body") + .first() + .locator(".DragList-row .DragList-content > span") + .allInnerTexts(); + + const typeName = names.find((name) => !inUseElsewhere.includes(name)); + if (!typeName) throw new Error("no type left to switch off"); + + const toggle = adminPage.getByRole("checkbox", { name: `Use ${typeName}` }); + await toggle.click(); + await expect(toggle).not.toBeChecked(); + + // Once again we must pass the modal to save instance-wide state + await adminPage.getByRole("button", { name: "Save Order" }).click(); + await adminPage.getByRole("button", { name: "Save for everyone" }).click(); + await expect(adminPage.getByText("Order saved.")).toBeVisible(); + await expect(toggle).not.toBeChecked(); + + // Now someone labeling an image will not be offered the disabled groups/types + const admin = await adminApi(); + const performer = await createPerformer(admin, { + name: uniq("DisabledPerf"), + }); + await admin.dispose(); + + await editPage.goto(`/performers/${performer.id}/edit`); + await editPage.waitForLoadState("networkidle"); + await editPage.getByRole("tab", { name: "Images" }).click(); + + await editPage + .locator('input[type="file"]') + .first() + .setInputFiles(tinyJpegPath()); + await editPage.getByRole("button", { name: "Upload" }).click(); + await expect( + editPage.getByRole("button", { name: "Upload" }), + ).toHaveCount(0, { + timeout: 15_000, + }); + + await editPage.locator(".ImageInput-image").first().click(); + await editPage.waitForSelector(".ImageLightbox-editor", { + timeout: 15_000, + }); + + await editPage.locator(".EditImages-labels-select").click(); + // Check that anything at all is offered so an empty select box doesn't pass the test + await expect( + editPage.locator(".react-select__option").first(), + ).toBeVisible(); + await expect( + editPage.locator(".react-select__option", { hasText: typeName }), + ).toHaveCount(0); + } finally { + await restoreEnabled(); + } +}); diff --git a/e2e/tests/entities/images.spec.ts b/e2e/tests/entities/images.spec.ts index 1a7c71765..22122e53e 100644 --- a/e2e/tests/entities/images.spec.ts +++ b/e2e/tests/entities/images.spec.ts @@ -1,14 +1,19 @@ -// Image upload + role gates. The upload path goes through the UI's -// EditImages component (file picker → imageCreate mutation → image_ids in -// the entity edit). Role-gate tests assert the @hasRole directives on -// imageCreate (EDIT) and imageDestroy (MODIFY) without touching the file -// backend. - +import type { Page } from "@playwright/test"; import { test, expect } from "../../support/fixtures"; -import { adminApi, createStudio, gql, uniq } from "../../support/helpers/seed"; +import { + adminApi, + createPerformer, + createStudio, + gql, + uniq, +} from "../../support/helpers/seed"; import { graphqlAs } from "../../support/helpers/graphql"; import { approveEdit } from "../../support/helpers/workflow"; import { tinyJpegPath } from "../../support/fixtures/tiny-jpeg"; +import { + squarePngPath, + SQUARE_PNG_SIZE, +} from "../../support/fixtures/square-png"; test("studio image upload via UI: edit lands with the uploaded image attached", async ({ editPage, @@ -22,19 +27,26 @@ test("studio image upload via UI: edit lands with the uploaded image attached", await editPage.waitForLoadState("networkidle"); await editPage.getByRole("tab", { name: "Images" }).click(); - // EditImages: file picker → "Upload" button → imageCreate mutation. Each - // step is explicit; setInputFiles alone doesn't fire the upload. + // EditImages: file picker → crop step → imageCreate mutation. Each step is + // explicit; setInputFiles alone does not fire the upload. The button reads + // "Upload" rather than "Crop and upload" because no frame has been chosen, + // which is what these tests want - the crop itself is covered separately const fileInput = editPage.locator('input[type="file"]').first(); await fileInput.setInputFiles(tinyJpegPath()); - await editPage.getByRole("button", { name: "Upload", exact: true }).click(); + await editPage.getByRole("button", { name: "Upload" }).click(); - // The "Uploading image..." spinner shows while the mutation is in flight; - // once it clears, the image preview is in place. - await expect(editPage.getByText(/Uploading image/i)).toHaveCount(0, { - timeout: 15_000, - }); + // The crop step disappears once the mutation lands and the image is in the + // form, so its buttons going away is the signal the upload finished. The + // match is deliberately loose: the button reads "Uploading..." in between, + // and an exact match would count it as already gone + await expect(editPage.getByRole("button", { name: "Upload" })).toHaveCount( + 0, + { + timeout: 15_000, + }, + ); - // Submit the edit from the Confirm tab. + // Submit the edit from the Confirm tab await editPage.getByRole("tab", { name: "Confirm" }).click(); await editPage.locator('textarea[name="note"]').fill("attach image via e2e"); await expect( @@ -47,10 +59,12 @@ test("studio image upload via UI: edit lands with the uploaded image attached", await approveEdit(moderatePage, editId); // Studio should now have at least one image with the dimensions libvips - // returned for our 1x1 JFIF. + // returned for our 1x1 JFIF const verify = await adminApi(); const data = await gql<{ - findStudio: { images: { id: string; width: number; height: number }[] } | null; + findStudio: { + images: { id: string; width: number; height: number }[]; + } | null; }>( verify, `query($id: ID!) { @@ -138,3 +152,225 @@ test("imageDestroy role gate: MODIFY allowed (resolver-level), EDIT denied", asy expect(modifierBody.errors[0].message).not.toMatch(/not authorized/i); } }); + +// Adds one label to whichever image the lightbox editor is focused on +const addLabel = async (page: Page, name: string) => { + const select = page.locator(".EditImages-labels-select"); + await select.click(); + await page + .locator(".react-select__option", { hasText: name }) + .first() + .click(); + await expect( + page.locator(".ImageLightbox-editor .tag-item", { hasText: name }), + ).toBeVisible(); +}; + +test("performer image labels via UI: dropdowns and date reach typed_images", async ({ + editPage, + moderatePage, +}) => { + const admin = await adminApi(); + const performer = await createPerformer(admin, { name: uniq("LabelPerf") }); + await admin.dispose(); + + await editPage.goto(`/performers/${performer.id}/edit`); + await editPage.waitForLoadState("networkidle"); + await editPage.getByRole("tab", { name: "Images" }).click(); + + // Uploaded rather than seeded from a URL: images are stored files, and every + // URL-only image shares the empty checksum, so only one can exist at a time + await editPage + .locator('input[type="file"]') + .first() + .setInputFiles(tinyJpegPath()); + await editPage.getByRole("button", { name: "Upload" }).click(); + await expect(editPage.getByRole("button", { name: "Upload" })).toHaveCount( + 0, + { + timeout: 15_000, + }, + ); + + // Labelling happens in the lightbox, on one image at a time + await editPage.locator(".ImageInput-image").first().click(); + await editPage.waitForSelector(".ImageLightbox-editor", { timeout: 15_000 }); + + await addLabel(editPage, "Portrait"); + await addLabel(editPage, "Face"); + await editPage.getByLabel("Image date").fill("2019-06"); + + await editPage.locator(".ImageLightbox-close").click(); + + await editPage.getByRole("tab", { name: "Confirm" }).click(); + await editPage.locator('textarea[name="note"]').fill("label image via e2e"); + await expect( + editPage.getByRole("button", { name: "Submit Edit" }), + ).toBeEnabled({ timeout: 15_000 }); + await editPage.getByRole("button", { name: "Submit Edit" }).click(); + await editPage.waitForURL(/\/edits\/[0-9a-f-]+/i, { timeout: 15_000 }); + const editId = editPage.url().split("/").pop()!; + + await approveEdit(moderatePage, editId); + + const verify = await adminApi(); + const data = await gql<{ + findPerformer: { + typed_images: { types: string[]; date: string | null }[]; + } | null; + }>( + verify, + `query($id: ID!) { + findPerformer(id: $id) { typed_images { types date } } + }`, + { id: performer.id }, + ); + await verify.dispose(); + + const typed = data.findPerformer?.typed_images ?? []; + expect(typed).toHaveLength(1); + expect(typed[0].types.sort()).toEqual(["CROP_FACE", "SHOT_PORTRAIT"]); + expect(typed[0].date).toBe("2019-06"); +}); + +test("performer page shows each image's labels", async ({ + editPage, + moderatePage, +}) => { + const admin = await adminApi(); + const performer = await createPerformer(admin, { name: uniq("GalleryPerf") }); + await admin.dispose(); + + await editPage.goto(`/performers/${performer.id}/edit`); + await editPage.waitForLoadState("networkidle"); + await editPage.getByRole("tab", { name: "Images" }).click(); + + await editPage + .locator('input[type="file"]') + .first() + .setInputFiles(tinyJpegPath()); + await editPage.getByRole("button", { name: "Upload" }).click(); + await expect(editPage.getByRole("button", { name: "Upload" })).toHaveCount( + 0, + { + timeout: 15_000, + }, + ); + + await editPage.locator(".ImageInput-image").first().click(); + await editPage.waitForSelector(".ImageLightbox-editor", { timeout: 15_000 }); + await addLabel(editPage, "Candid"); + await editPage.getByLabel("Image date").fill("2021"); + await editPage.locator(".ImageLightbox-close").click(); + + await editPage.getByRole("tab", { name: "Confirm" }).click(); + await editPage.locator('textarea[name="note"]').fill("gallery labels e2e"); + await expect( + editPage.getByRole("button", { name: "Submit Edit" }), + ).toBeEnabled({ timeout: 15_000 }); + await editPage.getByRole("button", { name: "Submit Edit" }).click(); + await editPage.waitForURL(/\/edits\/[0-9a-f-]+/i, { timeout: 15_000 }); + await approveEdit(moderatePage, editPage.url().split("/").pop() ?? ""); + + await editPage.goto(`/performers/${performer.id}`); + await editPage.waitForLoadState("networkidle"); + + // On the performer page the labels live over the image in the lightbox + // rather than in a list beneath it: a labelled performer looks like an + // unlabelled one until you go looking + await editPage.locator(".performer-photo button.Image").click(); + const labels = editPage.locator(".ImageLightbox-main .ImageLightbox-labels"); + await expect(labels).toBeVisible(); + await expect(labels.getByText("Candid")).toBeVisible(); + await expect(labels.getByText("2021")).toBeVisible(); +}); + +test("performer image crop via UI: the frame drawn is the image stored", async ({ + editPage, + moderatePage, +}) => { + const admin = await adminApi(); + const performer = await createPerformer(admin, { name: uniq("CropPerf") }); + await admin.dispose(); + + await editPage.goto(`/performers/${performer.id}/edit`); + await editPage.waitForLoadState("networkidle"); + await editPage.getByRole("tab", { name: "Images" }).click(); + + await editPage + .locator('input[type="file"]') + .first() + .setInputFiles(squarePngPath()); + + await expect(editPage.getByRole("button", { name: "Upload" })).toBeVisible(); + + // Choosing the crop is what applies its template: one control, so a chosen + // frame and a chosen label cannot disagree + await editPage.locator(".CropStep .EditImages-labels-select").click(); + await editPage + .locator(".react-select__option", { hasText: "Face" }) + .first() + .click(); + + const handle = editPage.getByRole("button", { name: "Resize se" }); + await expect(handle).toBeVisible({ timeout: 15_000 }); + await expect( + editPage.getByRole("button", { name: "Crop and upload" }), + ).toBeVisible(); + + // A real pointer drag, which is the whole reason this test is here: the + // frame's geometry is unit-tested, but nothing else drives it through an + // actual browser + const box = await handle.boundingBox(); + if (!box) throw new Error("the resize handle has no box to drag"); + await editPage.mouse.move(box.x + box.width / 2, box.y + box.height / 2); + await editPage.mouse.down(); + await editPage.mouse.move(box.x - 60, box.y - 60, { steps: 10 }); + await editPage.mouse.up(); + + await editPage.getByRole("button", { name: "Crop and upload" }).click(); + await expect( + editPage.getByRole("button", { name: "Crop and upload" }), + ).toHaveCount(0, { timeout: 20_000 }); + + await editPage.getByRole("tab", { name: "Confirm" }).click(); + await editPage.locator('textarea[name="note"]').fill("crop image via e2e"); + await expect( + editPage.getByRole("button", { name: "Submit Edit" }), + ).toBeEnabled({ timeout: 15_000 }); + await editPage.getByRole("button", { name: "Submit Edit" }).click(); + await editPage.waitForURL(/\/edits\/[0-9a-f-]+/i, { timeout: 15_000 }); + await approveEdit(moderatePage, editPage.url().split("/").pop() ?? ""); + + const verify = await adminApi(); + const data = await gql<{ + findPerformer: { + typed_images: { + types: string[]; + image: { width: number; height: number }; + }[]; + } | null; + }>( + verify, + `query($id: ID!) { + findPerformer(id: $id) { + typed_images { types image { width height } } + } + }`, + { id: performer.id }, + ); + await verify.dispose(); + + const [stored] = data.findPerformer?.typed_images ?? []; + expect(stored?.types).toContain("CROP_FACE"); + + // The server cut what the frame described: the square went in, a portrait + // came out. Asserting the region rather than merely "something changed" is + // what makes this catch a crop applied to the wrong part of the image. + // The exact proportions are pinned by the Go integration tests and the crop + // arithmetic by its unit suites; portrait-out-of-a-square is what proves the + // journey cut the right region + const { width, height } = stored.image; + expect(width).toBeLessThan(SQUARE_PNG_SIZE); + expect(height).toBeGreaterThan(width); +}); diff --git a/frontend/src/App.scss b/frontend/src/App.scss index 2f92b7972..ba42bbc67 100644 --- a/frontend/src/App.scss +++ b/frontend/src/App.scss @@ -22,6 +22,9 @@ @import "./components/studioSelect/styles"; @import "./components/tagFilter/styles"; @import "./components/tagSelect/styles"; +@import "./components/dragList/styles"; +@import "./pages/imageTypes/styles"; +@import "./components/cropFrame/styles"; @import "./components/editImages/styles"; @import "./components/urlInput/styles"; @import "./components/image/styles"; diff --git a/frontend/src/Main.tsx b/frontend/src/Main.tsx index 3effac5c9..8071be6ba 100644 --- a/frontend/src/Main.tsx +++ b/frontend/src/Main.tsx @@ -12,6 +12,7 @@ import { ROUTE_EDITS, ROUTE_FORGOT_PASSWORD, ROUTE_HOME, + ROUTE_IMAGE_TYPES, ROUTE_LOGIN, ROUTE_LOGOUT, ROUTE_NOTIFICATIONS, @@ -151,6 +152,11 @@ const Main: FC = ({ children }) => { Sites + {isAdmin(user) && ( + + Image Types + + )} {isAdmin(user) && ( Audits diff --git a/frontend/src/components/cropFrame/CropFrame.tsx b/frontend/src/components/cropFrame/CropFrame.tsx new file mode 100644 index 000000000..5957338c7 --- /dev/null +++ b/frontend/src/components/cropFrame/CropFrame.tsx @@ -0,0 +1,353 @@ +import cx from "classnames"; +import { + type FC, + type KeyboardEvent as ReactKeyboardEvent, + type PointerEvent as ReactPointerEvent, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import { Button, Form } from "react-bootstrap"; +import type { CropGuide, CropShape } from "src/graphql"; + +import CropOverlay from "./CropOverlay"; +import { + type CropRect, + cropPixels, + type Handle, + type HoldPoints, + isRoundSize, + moveRect, + refitRect, + resizeRect, + rotatedSize, +} from "./geometry"; +import { holdPointsFor } from "./holds"; + +const CLASSNAME = "CropFrame"; +const HANDLES: Handle[] = ["nw", "ne", "sw", "se"]; + +const MAX_ANGLE = 90; + +const suspendsSnapping = (event: ReactPointerEvent) => + event.ctrlKey || event.metaKey; + +const SUSPEND_KEY = + typeof navigator !== "undefined" && + /Mac|iPhone|iPad|iPod/.test(navigator.userAgent) + ? "⌘" + : "Ctrl"; + +interface CropFrameProps { + /** Object URL of the image being cropped */ + src: string; + /** Natural size of that image, before any rotation */ + naturalWidth: number; + naturalHeight: number; + /** Width over height to lock the frame to, or undefined to drag freely */ + aspectRatio?: number; + guides?: CropGuide[]; + /** Outlines the template draws, if it has any */ + shapes?: CropShape[]; + value: CropRect; + onChange: (rect: CropRect) => void; +} + +/** + * An aspect-locked frame dragged over an image, with the chosen template's + * guides drawn inside it + * + * Presentational and controlled: the frame is described entirely by `value`, + * in fractions of the rotated image, which is exactly what the server accepts. + * Nothing here reads or writes a file because cropping happens on the server, and + * what this produces is the rectangle describing it + */ +const CropFrame: FC = ({ + src, + naturalWidth, + naturalHeight, + aspectRatio, + guides = [], + shapes = [], + value, + onChange, +}) => { + const stage = useRef(null); + const [dragging, setDragging] = useState(false); + // Which lines the current drag is holding, for the overlay to mark: + // this is only set while a Shift-resize is under way + const [held, setHeld] = useState(); + const [shiftDown, setShiftDown] = useState(false); + + // Shift shows what a resize would pivot about before the press + useEffect(() => { + const track = (event: KeyboardEvent) => setShiftDown(event.shiftKey); + // Blur too: tabbing away with the key down never delivers the keyup, and + // the cue would sit there marking a line nothing is holding + const clear = () => setShiftDown(false); + + window.addEventListener("keydown", track); + window.addEventListener("keyup", track); + window.addEventListener("blur", clear); + return () => { + window.removeEventListener("keydown", track); + window.removeEventListener("keyup", track); + window.removeEventListener("blur", clear); + }; + }, []); + + // During a drag the lines are whatever was read at the press, which cannot + // change halfway through. Outside one, Shift previews what a press would + // hold, but never during a drag that did not start with it. Otherwise pressing + // the key mid-drag would promise an anchoring that is not happening + const previewed = useMemo(() => holdPointsFor(guides), [guides]); + const marked = held ?? (shiftDown && !dragging ? previewed : undefined); + + // The stage is the rotated image's bounding box, which is what the frame's + // fractions are measured against. Rotation grows it, matching what the + // server does, so the frame the contributor drags is the frame that gets cut + const rotated = rotatedSize(naturalWidth, naturalHeight, value.angle); + const imageAspect = rotated.width / rotated.height; + + // Mirrors the server's rounding, so the number on screen is the number that comes back + const output = cropPixels(value, naturalWidth, naturalHeight); + + const drag = useCallback( + ( + event: ReactPointerEvent, + apply: (dx: number, dy: number) => CropRect, + ) => { + const box = stage.current?.getBoundingClientRect(); + if (!box || box.width === 0 || box.height === 0) return; + + event.preventDefault(); + event.stopPropagation(); + const target = event.currentTarget; + target.setPointerCapture(event.pointerId); + setDragging(true); + + const startX = event.clientX; + const startY = event.clientY; + + const move = (moveEvent: PointerEvent) => { + // Deltas from the press rather than from the last event: accumulating + // per-move deltas drifts, because each one is clamped on the way in + onChange( + apply( + (moveEvent.clientX - startX) / box.width, + (moveEvent.clientY - startY) / box.height, + ), + ); + }; + + const done = () => { + target.releasePointerCapture(event.pointerId); + target.removeEventListener("pointermove", move); + target.removeEventListener("pointerup", done); + target.removeEventListener("pointercancel", done); + setDragging(false); + setHeld(undefined); + }; + + target.addEventListener("pointermove", move); + target.addEventListener("pointerup", done); + target.addEventListener("pointercancel", done); + }, + [onChange], + ); + + const startMove = (event: ReactPointerEvent) => { + const from = value; + drag(event, (dx, dy) => moveRect(from, dx, dy)); + }; + + const startResize = ( + event: ReactPointerEvent, + handle: Handle, + ) => { + const from = value; + const hold = event.shiftKey ? holdPointsFor(guides) : undefined; + setHeld(hold); + + // Snapping is on unless suspended, which is the way every editor that has + // it works: Photoshop, Figma and Inkscape all snap by default and give you + // a key to hold when you want the tool to stop helping + const canvasWidth = suspendsSnapping(event) ? undefined : rotated.width; + + drag(event, (dx, dy) => + resizeRect({ + rect: from, + handle, + dx, + dy, + targetAspect: aspectRatio, + imageAspect, + hold, + // Snapping is in pixels so the resize needs to know how + // many the frame is measured against + canvasWidth, + }), + ); + }; + + // Turning reshapes the stage, so a frame that fitted a moment ago may not + // now: refitting keeps its centre, because that is where the subject is + const setAngle = (angle: number) => { + const turned = rotatedSize(naturalWidth, naturalHeight, angle); + onChange( + refitRect({ ...value, angle }, aspectRatio, turned.width / turned.height), + ); + }; + + // Arrow keys nudge the frame. Dragging is pointer-only otherwise, and a + // fine adjustment is easier to make a keypress at a time than by hand + const nudge = (event: ReactKeyboardEvent) => { + const step = event.shiftKey ? 0.05 : 0.005; + const by: Record = { + ArrowLeft: [-step, 0], + ArrowRight: [step, 0], + ArrowUp: [0, -step], + ArrowDown: [0, step], + }; + const delta = by[event.key]; + if (!delta) return; + + event.preventDefault(); + onChange(moveRect(value, delta[0], delta[1])); + }; + + // With no template there is no shape to hold and no line to line anything up + // against, so the frame is not drawn at all. A border and four handles that + // only ever select the whole picture are furniture. + const framed = aspectRatio !== undefined; + + // Applied to both the frame and the shade behind it, which have to describe + // the same rectangle from either side of the clip + const frameRect = { + left: `${value.x * 100}%`, + top: `${value.y * 100}%`, + width: `${value.width * 100}%`, + height: `${value.height * 100}%`, + }; + + return ( +
+
+
+ + {framed &&
} +
+ + {framed && ( +
+ + +
+ )} +
+ +
+

+ Hold{" "} + {guides.length > 0 && ( + <> + Shift to resize around the guides, or{" "} + + )} + {SUSPEND_KEY} to size freely. +

+ +

+ {value.angle !== 0 && "≈ "} + {output.width} × {output.height} px +

+
+ + {framed && ( + +
+ Straighten + +
+ setAngle(Number(event.target.value))} + /> +
+ )} +
+ ); +}; + +export default CropFrame; diff --git a/frontend/src/components/cropFrame/CropOverlay.tsx b/frontend/src/components/cropFrame/CropOverlay.tsx new file mode 100644 index 000000000..34b950a2f --- /dev/null +++ b/frontend/src/components/cropFrame/CropOverlay.tsx @@ -0,0 +1,185 @@ +import cx from "classnames"; +import type { FC } from "react"; +import { + type CropGuide, + CropGuideAxisEnum, + CropGuideRoleEnum, + type CropShape, +} from "src/graphql"; + +import { largestCenteredRect } from "./geometry"; +import { shapeBounds, shapePath } from "./shapePath"; + +const CLASSNAME = "CropOverlay"; + +export interface CropTemplateInfo { + aspectRatio: number; + guides: CropGuide[]; + shapes: CropShape[]; +} + +interface CropOverlayProps { + guides: CropGuide[]; + /** Outlines drawn on the template's own layers, if it has any */ + shapes?: CropShape[]; + /** + * Lines the current drag is holding still, as fractions of the frame. Marked + * so a resize that behaves differently also looks different + */ + held?: { x?: number; y?: number }; + /** + * Shrink the overlay to a box of the template's shape, centred, instead of + * filling whatever it is placed in + * + * For drawing over a finished image, whose proportions are its own and need + * not be the template's. Without this the geometry is stretched onto the + * picture and an image that does not fit the frame is drawn as though it + * does which is the one thing the overlay is there to disprove. + * + * Not wanted inside the cropping tool, where the box the overlay sits in is + * already the frame and already the right shape + */ + fit?: { templateAspect: number; imageAspect: number }; +} + +/** How far from filling its box before the frame edge is worth drawing */ +const INSET_EPSILON = 0.005; + +/** + * A template's guide lines, drawn over whatever box this is placed in + * + * Positioned with percentages rather than drawn into an SVG viewBox, so the + * lines stay one pixel wide whatever shape the box is. A stretched viewBox + * gives horizontal and vertical strokes different weights, which reads as a + * rendering fault + * + * Named lines are the ones meant to be lined up on (the eye line, where the + * thighs meet) and labelling the thirds and margins as well turns a frame into + * a wall of text. The rest still carry their name as a tooltip + * + * That is anchors and the pivot, not anchors alone. The eye line is the example + * this rule was written for and it is a reference, because the head and the + * chin are the hard limits in a headshot; keying the name off the role alone + * left the one line the template resizes about as the only unnamed thing in it + */ +const CropOverlay: FC = ({ + guides, + shapes = [], + held, + fit, +}) => { + // The largest box of the template's shape that fits the picture, centred: + // the same rectangle the cropping tool starts a frame at, and for the same + // reason. It is where the crop would be if it were being made now + const frame = fit + ? largestCenteredRect(fit.templateAspect, fit.imageAspect) + : undefined; + + // Only worth drawing an edge when there is something outside it. On an image + // that already fits, the border would land exactly on the picture's own edge + // and read as a stray line + const inset = + frame !== undefined && + (frame.width < 1 - INSET_EPSILON || frame.height < 1 - INSET_EPSILON); + + return ( +
+ {/* + One SVG for every outline, stretched over the box by a unit viewBox so + the fractions the template stores need no conversion. + + preserveAspectRatio="none" is deliberate: the template's canvas and the + frame drawn over the photograph are the same shape, so stretching to fill + is what puts the oval where the designer drew it. non-scaling-stroke then + undoes that stretch for the stroke alone, which is what keeps a line one + pixel wide in both directions - the same problem the guides avoid by + being positioned in percentages rather than drawn in here. + */} + {shapes.length > 0 && ( + + )} + + {shapes.map((shape) => { + const bounds = shape.label ? shapeBounds(shape) : undefined; + if (!bounds) return null; + + return ( + + {shape.label} + + ); + })} + + {guides.map((guide) => { + const vertical = guide.axis === CropGuideAxisEnum.X; + const percent = `${guide.position * 100}%`; + const anchor = guide.role === CropGuideRoleEnum.ANCHOR; + const holdAt = vertical ? held?.x : held?.y; + const isHeld = + holdAt !== undefined && Math.abs(holdAt - guide.position) < 1e-6; + + return ( +
+ {(anchor || guide.pivot) && guide.label && ( + {guide.label} + )} +
+ ); + })} +
+ ); +}; + +export default CropOverlay; diff --git a/frontend/src/components/cropFrame/__tests__/CropFrame.test.tsx b/frontend/src/components/cropFrame/__tests__/CropFrame.test.tsx new file mode 100644 index 000000000..86ba26add --- /dev/null +++ b/frontend/src/components/cropFrame/__tests__/CropFrame.test.tsx @@ -0,0 +1,432 @@ +import { cleanup, fireEvent, render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import type { FC } from "react"; +import { useState } from "react"; +import { + type CropGuide, + CropGuideAxisEnum, + CropGuideRoleEnum, +} from "src/graphql"; +import { describe, expect, it, vi } from "vitest"; + +import CropFrame from "../CropFrame"; +import { type CropRect, FULL_FRAME } from "../geometry"; + +const SERVER_MAX_ANGLE = 90; + +const setup = ( + value: CropRect = FULL_FRAME, + onChange = vi.fn(), + templated = true, +) => { + const utils = render( + , + ); + return { ...utils, onChange, user: userEvent.setup() }; +}; + +const slider = () => screen.getByLabelText("Straighten") as HTMLInputElement; + +describe("CropFrame rotation", () => { + it("reaches as far as the server allows, both ways", () => { + setup(); + + expect(Number(slider().max)).toBe(SERVER_MAX_ANGLE); + expect(Number(slider().min)).toBe(-SERVER_MAX_ANGLE); + }); + + // A quarter turn covers the picture taken sideways; a tenth of a degree + // covers the horizon that is barely out. The step is what arrow keys move + // by, so it has to stay fine even though the range is wide + it("shows the current angle", () => { + setup({ ...FULL_FRAME, angle: 12.5 }); + expect(screen.getByRole("button", { name: /12\.5°/ })).toBeInTheDocument(); + }); + + it("returns to zero when the readout is clicked", async () => { + const { onChange, user } = setup({ ...FULL_FRAME, angle: 37 }); + + await user.click(screen.getByRole("button", { name: /37\.0°/ })); + + expect(onChange).toHaveBeenCalledTimes(1); + expect(onChange.mock.calls[0][0]).toMatchObject({ angle: 0 }); + }); + + it("offers nothing to reset when already straight", () => { + setup({ ...FULL_FRAME, angle: 0 }); + expect(screen.getByRole("button", { name: /0\.0°/ })).toBeDisabled(); + }); + + // Turning reshapes the stage, so the frame is refitted rather than left + // hanging outside the image. A quarter turn of a 200x300 picture makes it + // 300x200, which a full-frame crop cannot survive unchanged + it("refits the frame when the angle changes", () => { + const onChange = vi.fn(); + const { rerender } = render( + , + ); + rerender( + , + ); + + // The stage follows the rotated bounding box, so a portrait picture turned + // a quarter turn presents a landscape one to crop from + // + // Compared as a number, not a string: cos(90°) is not exactly zero in + // floating point, so the height comes out as 200.00000000000003. Correct + // to fourteen figures and invisible at any screen size + const stage = document.querySelector(".CropFrame-stage") as HTMLElement; + const [width, height] = stage.style.aspectRatio.split("/").map(Number); + expect(width / height).toBeCloseTo(300 / 200); + }); +}); + +describe("CropFrame shift-resize", () => { + // As CROP_FACE ships it: the eye line is soft, because the head and the chin + // are the hard limits, and is still what a resize turns about. Wiring that + // reads role instead of pivot would find nothing here. + const FACE: CropGuide[] = [ + { + __typename: "CropGuide" as const, + axis: CropGuideAxisEnum.Y, + position: 0.425, + role: CropGuideRoleEnum.REFERENCE, + label: "Bisects the eyes", + pivot: true, + }, + ]; + + const dragCorner = (shiftKey: boolean) => { + const onChange = vi.fn(); + const start: CropRect = { + x: 0.2, + y: 0.2, + width: 0.5, + height: 0.5, + angle: 0, + }; + + render( + , + ); + + // jsdom measures everything as zero and the drag needs a box to work + // against or it declines to start + const stage = document.querySelector(".CropFrame-stage") as HTMLElement; + stage.getBoundingClientRect = () => + ({ width: 400, height: 600, left: 0, top: 0 }) as DOMRect; + + const handle = screen.getByRole("button", { name: "Resize se" }); + fireEvent.pointerDown(handle, { + pointerId: 1, + clientX: 0, + clientY: 0, + shiftKey, + }); + // Both axes: the frame is shape-locked so the width leads and a purely + // vertical drag correctly changes nothing + fireEvent.pointerMove(handle, { pointerId: 1, clientX: 80, clientY: 120 }); + + return { onChange, start }; + }; + + const eyeLineOn = (rect: CropRect) => rect.y + 0.425 * rect.height; + + it("keeps the eye line still when Shift is held", () => { + const { onChange, start } = dragCorner(true); + + expect(onChange).toHaveBeenCalled(); + const resized = onChange.mock.calls.at(-1)?.[0] as CropRect; + expect(resized.height).toBeGreaterThan(start.height); + expect(eyeLineOn(resized)).toBeCloseTo(eyeLineOn(start)); + }); + + it("anchors the opposite corner without it", () => { + const { onChange, start } = dragCorner(false); + + const resized = onChange.mock.calls.at(-1)?.[0] as CropRect; + expect(resized.height).toBeGreaterThan(start.height); + expect(resized.y).toBeCloseTo(start.y); + expect(eyeLineOn(resized)).not.toBeCloseTo(eyeLineOn(start)); + }); + + const renderFrame = () => { + render( + , + ); + }; + + const marked = () => + document.querySelectorAll(".CropOverlay-guide-held").length; + + it("marks nothing until Shift is down", () => { + renderFrame(); + expect(marked()).toBe(0); + }); + + it("marks the line a resize would turn about while Shift is down", () => { + renderFrame(); + + fireEvent.keyDown(window, { key: "Shift", shiftKey: true }); + expect(marked()).toBe(1); + + fireEvent.keyUp(window, { key: "Shift", shiftKey: false }); + expect(marked()).toBe(0); + }); + + it("stops marking when the window loses focus", () => { + renderFrame(); + + fireEvent.keyDown(window, { key: "Shift", shiftKey: true }); + expect(marked()).toBe(1); + + fireEvent.blur(window); + expect(marked()).toBe(0); + }); + + it("does not start marking mid-drag", () => { + const onChange = vi.fn(); + render( + , + ); + + const stage = document.querySelector(".CropFrame-stage") as HTMLElement; + stage.getBoundingClientRect = () => + ({ width: 400, height: 600, left: 0, top: 0 }) as DOMRect; + + const handle = screen.getByRole("button", { name: "Resize se" }); + fireEvent.pointerDown(handle, { + pointerId: 1, + clientX: 0, + clientY: 0, + shiftKey: false, + }); + + fireEvent.keyDown(window, { key: "Shift", shiftKey: true }); + expect(marked()).toBe(0); + }); +}); + +describe("CropFrame without a template", () => { + it("draws no frame at all", () => { + setup(FULL_FRAME, vi.fn(), false); + + expect(document.querySelector(".CropFrame-frame")).toBeNull(); + expect(document.querySelector(".CropFrame-shade")).toBeNull(); + expect(screen.queryByRole("button", { name: "Resize se" })).toBeNull(); + }); + + it("does not offer the rotation control", () => { + setup(FULL_FRAME, vi.fn(), false); + expect(screen.queryByLabelText("Straighten")).toBeNull(); + }); +}); + +/** + * The size of the finished image, shown before it is sent. Contributors care + * about this to the pixel -- a frame nudged until it reads 1000 x 1500 is worth + * more than the same frame landing on 1012 x 1518 -- so the number has to + * follow the frame rather than describe the file that was picked. + */ +describe("CropFrame size readout", () => { + const readout = () => + document.querySelector(".CropFrame-size")?.textContent ?? ""; + + it("measures the frame, not the file", () => { + setup(FULL_FRAME); + expect(readout()).toBe("200 × 300 px"); + }); + + it("follows the frame as it shrinks", () => { + setup({ x: 0.25, y: 0.25, width: 0.5, height: 0.5, angle: 0 }); + expect(readout()).toBe("100 × 150 px"); + }); + + // libvips rounds the grown canvas itself before the fractions are measured + // against it, so a turned crop can land a pixel either side of this -- and + // only a turned one, so the untouched case stays unqualified. + it("admits to being approximate once turned, and only then", () => { + setup({ ...FULL_FRAME, angle: 10 }); + expect(readout()).toMatch(/^≈ /); + + cleanup(); + setup(FULL_FRAME); + expect(readout()).not.toMatch(/^≈ /); + }); + + // Rotation changes the size on its own, and an untouched upload still has a + // size worth knowing before it is sent. + it("is there without a template", () => { + setup(FULL_FRAME, vi.fn(), false); + expect(readout()).toBe("200 × 300 px"); + }); +}); + +/** + * That the snap reaches the readout, which is the seam between the arithmetic + * and the thing anyone actually sees. Held in state so the drag, the snap and + * the number form one loop, rather than checking the emitted rectangle and + * assuming the rest. + */ +describe("CropFrame snapping to round sizes", () => { + const Controlled: FC<{ start: CropRect }> = ({ start }) => { + const [rect, setRect] = useState(start); + return ( + + ); + }; + + const dragTo = (clientX: number, modifiers: Record = {}) => { + render(); + + // jsdom measures everything as zero, and the drag needs a box to work + // against or it declines to start. + const stage = document.querySelector(".CropFrame-stage") as HTMLElement; + stage.getBoundingClientRect = () => + ({ width: 400, height: 600, left: 0, top: 0 }) as DOMRect; + + const handle = screen.getByRole("button", { name: "Resize se" }); + fireEvent.pointerDown(handle, { + pointerId: 1, + clientX: 0, + clientY: 0, + ...modifiers, + }); + fireEvent.pointerMove(handle, { pointerId: 1, clientX, clientY: 0 }); + + return document.querySelector(".CropFrame-size") as HTMLElement; + }; + + // -264 of 400 is 0.66 of the frame, leaving 1020 of 3000 pixels: near enough + // to 1000 that someone was plainly aiming at it. + it("lands the readout on a round size", () => { + expect(dragTo(-264).textContent).toBe("1000 × 1500 px"); + }); + + it("marks the readout once it lands", () => { + expect(dragTo(-264).className).toContain("CropFrame-size-round"); + }); + + // Two percent of the frame. Further than that and the frame is not nearly + // right, it is somewhere else. + it("leaves the readout unmarked when the frame is between sizes", () => { + const readout = dragTo(-261); + expect(readout.textContent).not.toBe("1000 × 1500 px"); + expect(readout.className).not.toContain("CropFrame-size-round"); + }); +}); + +/** + * Snapping is on by default and a held key suspends it, which is how every + * editor that has snapping works -- Photoshop, Figma and Inkscape all snap + * unless told not to. Nobody holds a key to *get* snapping. + * + * Both Ctrl and Command count. Ctrl-clicking is a secondary click on macOS and + * would never arrive here, so a Ctrl-only check would leave Mac users with no + * way to switch snapping off at all. + */ +describe("CropFrame suspending the snap", () => { + const Controlled: FC<{ start: CropRect }> = ({ start }) => { + const [rect, setRect] = useState(start); + return ( + + ); + }; + + const dragWith = (modifiers: Record) => { + render(); + + const stage = document.querySelector(".CropFrame-stage") as HTMLElement; + stage.getBoundingClientRect = () => + ({ width: 400, height: 600, left: 0, top: 0 }) as DOMRect; + + const handle = screen.getByRole("button", { name: "Resize se" }); + fireEvent.pointerDown(handle, { + pointerId: 1, + clientX: 0, + clientY: 0, + ...modifiers, + }); + fireEvent.pointerMove(handle, { pointerId: 1, clientX: -264, clientY: 0 }); + + return document.querySelector(".CropFrame-size")?.textContent ?? ""; + }; + + // The same drag that lands on 1000 x 1500 unmodified. + it("snaps when nothing is held", () => { + expect(dragWith({})).toBe("1000 × 1500 px"); + }); + + it("does not snap while Ctrl is held", () => { + expect(dragWith({ ctrlKey: true })).toBe("1020 × 1530 px"); + }); + + it("does not snap while Command is held either", () => { + expect(dragWith({ metaKey: true })).toBe("1020 × 1530 px"); + }); + + // Shift already means something else here, and the two have to be + // independently usable: holding a guide is not a request to stop snapping. + it("still snaps while Shift is held", () => { + expect(dragWith({ shiftKey: true })).toBe("1000 × 1500 px"); + }); +}); diff --git a/frontend/src/components/cropFrame/__tests__/CropOverlay.test.tsx b/frontend/src/components/cropFrame/__tests__/CropOverlay.test.tsx new file mode 100644 index 000000000..76c39b49c --- /dev/null +++ b/frontend/src/components/cropFrame/__tests__/CropOverlay.test.tsx @@ -0,0 +1,228 @@ +import { render } from "@testing-library/react"; +import { + type CropGuide, + CropGuideAxisEnum, + CropGuideRoleEnum, + type CropShape, +} from "src/graphql"; +import { describe, expect, it } from "vitest"; +import CropOverlay from "../CropOverlay"; + +const guide = ( + axis: CropGuideAxisEnum, + position: number, + role: CropGuideRoleEnum | null, + label: string | null, + pivot = false, +): CropGuide => ({ + __typename: "CropGuide" as const, + axis, + position, + role, + label, + pivot, +}); + +const GUIDES: CropGuide[] = [ + guide(CropGuideAxisEnum.X, 0.015, CropGuideRoleEnum.MARGIN, "Left margin"), + guide(CropGuideAxisEnum.X, 0.5, CropGuideRoleEnum.REFERENCE, "Centre"), + guide( + CropGuideAxisEnum.Y, + 0.425, + CropGuideRoleEnum.ANCHOR, + "Bisects the eyes", + ), + guide(CropGuideAxisEnum.Y, 0.77, CropGuideRoleEnum.REFERENCE, "Chin"), + guide(CropGuideAxisEnum.Y, 0.9, null, null), +]; + +const lines = (container: HTMLElement) => + Array.from(container.querySelectorAll(".CropOverlay-guide")); + +describe("CropOverlay", () => { + it("draws every guide the template carries", () => { + const { container } = render(); + expect(lines(container)).toHaveLength(GUIDES.length); + }); + + it("draws nothing for a template with no guides", () => { + const { container } = render(); + expect(lines(container)).toHaveLength(0); + }); + + // A vertical guide is placed across the width and a horizontal one down the + // height. Putting a position on the wrong axis is the mistake that makes an + // overlay look plausible and be wrong. + it("places each guide along its own axis", () => { + const { container } = render(); + const drawn = lines(container) as HTMLElement[]; + + const vertical = drawn.filter((line) => + line.classList.contains("CropOverlay-guide-vertical"), + ); + const horizontal = drawn.filter((line) => + line.classList.contains("CropOverlay-guide-horizontal"), + ); + + expect(vertical).toHaveLength(2); + expect(horizontal).toHaveLength(3); + + expect(vertical[0].style.left).toBe("1.5%"); + expect(vertical[0].style.top).toBe(""); + expect(horizontal[0].style.top).toBe("42.5%"); + expect(horizontal[0].style.left).toBe(""); + }); + + // Anchors are the lines meant to be hit, so they are the ones worth reading + // on the image. Naming the thirds and margins as well turns a frame into a + // wall of text. + it("names anchors on the image and leaves the rest to a tooltip", () => { + const { container, queryByText } = render(); + + expect(queryByText("Bisects the eyes")).not.toBeNull(); + expect(queryByText("Chin")).toBeNull(); + expect(queryByText("Left margin")).toBeNull(); + + const titles = lines(container).map((line) => line.getAttribute("title")); + expect(titles).toContain("Chin"); + expect(titles).toContain("Left margin"); + }); + + // The pivot is named whatever its role. It is the line the frame resizes + // about, so it is by definition one to line up on -- and in the face + // template it is a reference, because the head and the chin are the hard + // limits there. Keying the name off the role alone left the one line the + // template turns about as the only unnamed thing on it. + it("names the pivot even when it is only a reference", () => { + const { queryByText } = render( + , + ); + + expect(queryByText("Bisects the eyes")).not.toBeNull(); + // And still nothing for the reference that is not the pivot. + expect(queryByText("Chin")).toBeNull(); + }); + + // A template need not say how closely each line should be followed, and an + // unnamed guide is still a usable one. + it("draws a guide with no role or label", () => { + const { container } = render( + , + ); + + const drawn = lines(container) as HTMLElement[]; + expect(drawn).toHaveLength(1); + expect(drawn[0].style.top).toBe("50%"); + expect(drawn[0].getAttribute("title")).toBeNull(); + }); +}); + +/** + * Outlines drawn on a template's own layers, rather than dragged off a ruler. + * A template whose whole content is an oval for a face has no guides at all, + * so this is the only thing it draws. + */ +describe("CropOverlay shapes", () => { + const point = (x: number, y: number) => ({ + __typename: "CropPoint" as const, + x, + y, + }); + const corner = (x: number, y: number) => ({ + __typename: "CropKnot" as const, + control_in: point(x, y), + anchor: point(x, y), + control_out: point(x, y), + }); + const shape = (label: string | null): CropShape => ({ + __typename: "CropShape" as const, + label, + subpaths: [ + { + __typename: "CropSubpath" as const, + closed: true, + knots: [corner(0.1, 0.1), corner(0.9, 0.1), corner(0.9, 0.9)], + }, + ], + }); + + const paths = () => document.querySelectorAll(".CropOverlay-shape"); + + it("draws one path per shape", () => { + render(); + expect(paths()).toHaveLength(2); + }); + + it("gives each path its outline", () => { + render(); + expect(paths()[0].getAttribute("d")).toContain("M0.1,0.1"); + }); + + it("names a shape that has a name", () => { + render(); + expect(paths()[0].querySelector("title")?.textContent).toBe("head guide"); + }); + + it("draws nothing at all without shapes", () => { + render(); + expect(document.querySelector(".CropOverlay-shapes")).toBeNull(); + }); +}); + +describe("CropOverlay shape labels", () => { + const point = (x: number, y: number) => ({ + __typename: "CropPoint" as const, + x, + y, + }); + const corner = (x: number, y: number) => ({ + __typename: "CropKnot" as const, + // Controls well outside the outline, as an ellipse's are. Including them + // in the extent would put the label where the shape never goes. + control_in: point(x - 0.3, y - 0.3), + anchor: point(x, y), + control_out: point(x + 0.3, y + 0.3), + }); + const oval = (label: string | null): CropShape => ({ + __typename: "CropShape" as const, + label, + subpaths: [ + { + __typename: "CropSubpath" as const, + closed: true, + knots: [corner(0.2, 0.1), corner(0.8, 0.1), corner(0.8, 0.7)], + }, + ], + }); + + const label = () => + document.querySelector(".CropOverlay-shape-label") as HTMLElement; + + it("names a shape on the overlay, not only in a tooltip", () => { + render(); + expect(label().textContent).toBe("head guide"); + }); + + // Centred across the outline and on its top edge, from the anchors alone. + it("hangs the name on the crown of the outline", () => { + render(); + expect(label().style.left).toBe("50%"); + expect(Number.parseFloat(label().style.top)).toBeCloseTo(10, 4); + }); + + it("says nothing about an unnamed shape", () => { + render(); + expect(document.querySelector(".CropOverlay-shape-label")).toBeNull(); + }); +}); diff --git a/frontend/src/components/cropFrame/__tests__/geometry.test.ts b/frontend/src/components/cropFrame/__tests__/geometry.test.ts new file mode 100644 index 000000000..919ed944b --- /dev/null +++ b/frontend/src/components/cropFrame/__tests__/geometry.test.ts @@ -0,0 +1,648 @@ +import { describe, expect, it } from "vitest"; +import { + type CropRect, + cropPixels, + FULL_FRAME, + heightForWidth, + isRoundSize, + largestCenteredRect, + moveRect, + refitRect, + resizeRect, + rotatedSize, + snapWidth, + widthForHeight, +} from "../geometry"; + +const isInside = (rect: CropRect) => + rect.x >= 0 && + rect.y >= 0 && + rect.width > 0 && + rect.height > 0 && + rect.x + rect.width <= 1 + 1e-9 && + rect.y + rect.height <= 1 + 1e-9; + +/** + * The pixel aspect a frame actually has, which is the thing being locked + * + * Not `width / height`: those are fractions of two different spans, so a + * half-by-half frame on a 2:3 image is 2:3, not square. Every one of these + * tests would pass against a broken implementation if it compared the + * fractions directly + */ +const aspectOf = (rect: CropRect, imageAspect: number) => + (rect.width * imageAspect) / rect.height; + +const PORTRAIT = 2 / 3; +const LANDSCAPE = 16 / 9; + +describe("rotatedSize", () => { + it("leaves an unturned image alone", () => { + expect(rotatedSize(200, 300, 0)).toEqual({ width: 200, height: 300 }); + }); + + it("swaps the sides on a quarter turn", () => { + const turned = rotatedSize(200, 300, 90); + expect(turned.width).toBeCloseTo(300); + expect(turned.height).toBeCloseTo(200); + }); + + it("grows the canvas rather than clipping the corners", () => { + const turned = rotatedSize(200, 300, 10); + expect(turned.width).toBeGreaterThan(200); + expect(turned.height).toBeGreaterThan(300); + // And by the same amount either way. + expect(rotatedSize(200, 300, -10)).toEqual(turned); + }); +}); + +describe("largestCenteredRect", () => { + it("keeps the whole image when nothing is locked", () => { + expect(largestCenteredRect(undefined, PORTRAIT)).toEqual(FULL_FRAME); + }); + + it("fills an image that already has the wanted shape", () => { + const rect = largestCenteredRect(PORTRAIT, PORTRAIT); + expect(rect.width).toBeCloseTo(1); + expect(rect.height).toBeCloseTo(1); + }); + + it("is centred and inside, whatever the shapes", () => { + for (const target of [PORTRAIT, LANDSCAPE, 1, 3 / 4]) { + for (const image of [PORTRAIT, LANDSCAPE, 1, 9 / 16]) { + const rect = largestCenteredRect(target, image); + + expect(isInside(rect)).toBe(true); + expect(aspectOf(rect, image)).toBeCloseTo(target); + expect(rect.x + rect.width / 2).toBeCloseTo(0.5); + expect(rect.y + rect.height / 2).toBeCloseTo(0.5); + + // Largest, so one side has to be touching. + expect(Math.max(rect.width, rect.height)).toBeCloseTo(1); + } + } + }); +}); + +describe("heightForWidth / widthForHeight", () => { + it("gives a frame the aspect asked for, and inverts", () => { + const height = heightForWidth(0.5, PORTRAIT, LANDSCAPE); + const rect = { ...FULL_FRAME, width: 0.5, height }; + expect(aspectOf(rect, LANDSCAPE)).toBeCloseTo(PORTRAIT); + expect(widthForHeight(height, PORTRAIT, LANDSCAPE)).toBeCloseTo(0.5); + }); +}); + +describe("moveRect", () => { + const rect: CropRect = { + x: 0.25, + y: 0.25, + width: 0.5, + height: 0.5, + angle: 0, + }; + + it("slides by the delta", () => { + expect(moveRect(rect, 0.1, -0.1)).toMatchObject({ x: 0.35, y: 0.15 }); + }); + + it("stops at the edges rather than leaving the image", () => { + expect(moveRect(rect, -10, -10)).toMatchObject({ x: 0, y: 0 }); + expect(moveRect(rect, 10, 10)).toMatchObject({ x: 0.5, y: 0.5 }); + }); + + // The shape is usually what someone cares about most, so hitting a border + // must not quietly resize the frame. + it("keeps the size when it hits a border", () => { + const pushed = moveRect(rect, 10, 10); + expect(pushed.width).toBe(rect.width); + expect(pushed.height).toBe(rect.height); + }); +}); + +describe("resizeRect", () => { + const start: CropRect = { x: 0.2, y: 0.2, width: 0.6, height: 0.6, angle: 0 }; + + it("holds the opposite corner still", () => { + const resized = resizeRect({ + rect: start, + handle: "se", + dx: -0.1, + dy: -0.1, + targetAspect: undefined, + imageAspect: 1, + }); + expect(resized.x).toBeCloseTo(0.2); + expect(resized.y).toBeCloseTo(0.2); + expect(resized.width).toBeLessThan(start.width); + }); + + it("grows from the anchored corner when dragged north-west", () => { + const resized = resizeRect({ + rect: start, + handle: "nw", + dx: -0.1, + dy: -0.1, + targetAspect: undefined, + imageAspect: 1, + }); + expect(resized.x + resized.width).toBeCloseTo(0.8); + expect(resized.y + resized.height).toBeCloseTo(0.8); + }); + + // The point of locking: a frame that could be pulled out of shape at the + // edges produces uploads that miss the ratio, which is what a template + // exists to prevent + it("holds the aspect through every corner and drag", () => { + for (const handle of ["nw", "ne", "sw", "se"] as const) { + for (const dx of [-0.5, -0.2, -0.01, 0.01, 0.2, 0.5]) { + for (const dy of [-0.5, -0.2, -0.01, 0.01, 0.2, 0.5]) { + for (const image of [PORTRAIT, LANDSCAPE, 1]) { + const resized = resizeRect({ + rect: start, + handle: handle, + dx: dx, + dy: dy, + targetAspect: PORTRAIT, + imageAspect: image, + }); + + expect(isInside(resized)).toBe(true); + expect(aspectOf(resized, image)).toBeCloseTo(PORTRAIT, 3); + } + } + } + } + }); + + it("stays inside the image when unlocked too", () => { + for (const handle of ["nw", "ne", "sw", "se"] as const) { + for (const dx of [-2, -0.3, 0.3, 2]) { + for (const dy of [-2, -0.3, 0.3, 2]) { + expect( + isInside( + resizeRect({ + rect: start, + handle: handle, + dx: dx, + dy: dy, + targetAspect: undefined, + imageAspect: 1, + }), + ), + ).toBe(true); + } + } + } + }); +}); + +describe("refitRect", () => { + it("restores the shape after the image changes proportions", () => { + const rect: CropRect = { + x: 0.1, + y: 0.1, + width: 0.8, + height: 0.8, + angle: 5, + }; + const refitted = refitRect(rect, PORTRAIT, LANDSCAPE); + + expect(isInside(refitted)).toBe(true); + expect(aspectOf(refitted, LANDSCAPE)).toBeCloseTo(PORTRAIT); + }); + + // Straightening would be unusable if the frame jumped back to the middle + // every time the angle nudged. + it("keeps the frame where it was", () => { + const rect: CropRect = { + x: 0.05, + y: 0.05, + width: 0.4, + height: 0.4, + angle: 0, + }; + const refitted = refitRect(rect, undefined, 1); + + expect(refitted.x + refitted.width / 2).toBeCloseTo( + rect.x + rect.width / 2, + ); + expect(refitted.y + refitted.height / 2).toBeCloseTo( + rect.y + rect.height / 2, + ); + }); + + it("carries the angle through", () => { + expect(refitRect({ ...FULL_FRAME, angle: 7 }, undefined, 1).angle).toBe(7); + }); + + it("pulls an oversized frame back inside", () => { + const refitted = refitRect( + { x: 0.9, y: 0.9, width: 0.5, height: 0.5, angle: 0 }, + undefined, + 1, + ); + expect(isInside(refitted)).toBe(true); + }); +}); + +describe("resizeRect holding a guide", () => { + const start: CropRect = { x: 0.2, y: 0.2, width: 0.6, height: 0.6, angle: 0 }; + + /** Where a guide at fraction p of the frame lands on the image. */ + const onImage = (edge: number, size: number, p: number) => edge + p * size; + + // The whole point: line the eye line up on the eyes, then size the frame to + // the head without losing the alignment + it("keeps the held line where it was", () => { + const eyes = 0.425; + const held = onImage(start.y, start.height, eyes); + + for (const handle of ["nw", "ne", "sw", "se"] as const) { + for (const dy of [-0.3, -0.05, 0.05, 0.3]) { + const resized = resizeRect({ + rect: start, + handle, + dx: 0, + dy, + imageAspect: 1, + hold: { y: eyes }, + }); + expect(onImage(resized.y, resized.height, eyes)).toBeCloseTo(held); + } + } + }); + + it("holds both axes at once", () => { + const hold = { x: 0.5, y: 0.425 }; + const heldX = onImage(start.x, start.width, hold.x); + const heldY = onImage(start.y, start.height, hold.y); + + const resized = resizeRect({ + rect: start, + handle: "se", + dx: 0.1, + dy: 0.1, + targetAspect: undefined, + imageAspect: 1, + hold: hold, + }); + + expect(onImage(resized.x, resized.width, hold.x)).toBeCloseTo(heldX); + expect(onImage(resized.y, resized.height, hold.y)).toBeCloseTo(heldY); + }); + + // A held frame that could be pulled out of shape would defeat the template + // just as surely as an unheld one. + it("keeps the aspect, and the line, together", () => { + const hold = { x: 0.5, y: 0.425 }; + const heldY = onImage(start.y, start.height, hold.y); + + for (const handle of ["nw", "ne", "sw", "se"] as const) { + for (const d of [-0.4, -0.1, 0.1, 0.4]) { + const resized = resizeRect({ + rect: start, + handle, + dx: d, + dy: d, + targetAspect: PORTRAIT, + imageAspect: LANDSCAPE, + hold, + }); + + expect(isInside(resized)).toBe(true); + expect(aspectOf(resized, LANDSCAPE)).toBeCloseTo(PORTRAIT, 3); + expect(onImage(resized.y, resized.height, hold.y)).toBeCloseTo( + heldY, + 3, + ); + } + } + }); + + // Running out of room has to cost size, not alignment. Sliding the frame back + // inside would break the one thing this mode exists to keep. + it("stops growing rather than sliding off the line", () => { + // A line near the top: the frame can only grow so far before its top edge + // would leave the image. + const hold = { y: 0.9 }; + const near: CropRect = { + x: 0.1, + y: 0.02, + width: 0.5, + height: 0.1, + angle: 0, + }; + const held = onImage(near.y, near.height, hold.y); + + const resized = resizeRect({ + rect: near, + handle: "se", + dx: 0, + dy: 5, + targetAspect: undefined, + imageAspect: 1, + hold: hold, + }); + + expect(isInside(resized)).toBe(true); + expect(onImage(resized.y, resized.height, hold.y)).toBeCloseTo(held); + }); + + it("stays inside the image however it is dragged", () => { + for (const handle of ["nw", "ne", "sw", "se"] as const) { + for (const dx of [-2, -0.3, 0.3, 2]) { + for (const dy of [-2, -0.3, 0.3, 2]) { + for (const hold of [{ y: 0.1 }, { y: 0.9 }, { x: 0.5, y: 0.425 }]) { + const resized = resizeRect({ + rect: start, + handle, + dx, + dy, + imageAspect: 1, + hold, + }); + expect(isInside(resized)).toBe(true); + } + } + } + } + }); + + // A guide at the very edge of the frame only constrains one side, and + // dividing by its distance to the other would be a division by zero. + it("copes with a line on the frame's own edge", () => { + for (const p of [0, 1]) { + const resized = resizeRect({ + rect: start, + handle: "se", + dx: 0.2, + dy: 0.2, + targetAspect: undefined, + imageAspect: 1, + hold: { y: p }, + }); + expect(isInside(resized)).toBe(true); + expect(Number.isFinite(resized.y)).toBe(true); + expect(Number.isFinite(resized.height)).toBe(true); + } + }); + + // Without a hold it must behave exactly as before: the opposite corner stays. + it("falls back to corner anchoring when nothing is held", () => { + const plain = resizeRect({ + rect: start, + handle: "se", + dx: -0.1, + dy: -0.1, + targetAspect: undefined, + imageAspect: 1, + }); + const empty = resizeRect({ + rect: start, + handle: "se", + dx: -0.1, + dy: -0.1, + targetAspect: undefined, + imageAspect: 1, + hold: {}, + }); + + expect(empty).toEqual(plain); + expect(plain.x).toBeCloseTo(start.x); + expect(plain.y).toBeCloseTo(start.y); + }); +}); + +/** + * The counterpart of TestCropRectPixels and TestCropRectPixelsStayInsideTheImage + * in internal/image/crop_test.go, using the same cases against the same sizes. + * Two mirrors of one calculation, so a change to either that is not made to the + * other shows up as a failure rather than as a number on screen that quietly + * stops being true. + */ +describe("cropPixels", () => { + const at = (rect: Partial, width = 800, height = 1200) => + cropPixels({ ...FULL_FRAME, ...rect }, width, height); + + it("gives the whole image back for the whole frame", () => { + expect(at({})).toEqual({ left: 0, top: 0, width: 800, height: 1200 }); + }); + + it("measures a quarter frame wherever it sits", () => { + expect(at({ x: 0, y: 0, width: 0.5, height: 0.5 })).toEqual({ + left: 0, + top: 0, + width: 400, + height: 600, + }); + expect(at({ x: 0.5, y: 0.5, width: 0.5, height: 0.5 })).toEqual({ + left: 400, + top: 600, + width: 400, + height: 600, + }); + expect(at({ x: 0.25, y: 0.25, width: 0.5, height: 0.5 })).toEqual({ + left: 200, + top: 300, + width: 400, + height: 600, + }); + }); + + // Whole pixels, because that is what comes out the other end. Go rounds half + // away from zero and JavaScript rounds half upward; every value here is + // positive, which is what makes the two the same rule. + it("rounds to whole pixels", () => { + expect(at({ width: 1 / 3, height: 1 / 3 })).toEqual({ + left: 0, + top: 0, + width: 267, + height: 400, + }); + }); + + // Rotation grows the canvas rather than clipping the corners, so a quarter + // turn swaps the two dimensions. + it("follows the canvas a rotation grows", () => { + expect(at({ angle: 90 }, 1000, 1500)).toMatchObject({ + width: 1500, + height: 1000, + }); + expect(at({ angle: 45 }, 1000, 1000)).toMatchObject({ + width: 1414, + height: 1414, + }); + }); + + // The far edge is what gets checked, not the size: a frame at the bottom + // corner has a size that fits the image comfortably while still reaching + // outside it, so asserting on the size alone would miss the bug the clamps + // are there to prevent. + it("never reaches past the edge, and never asks for nothing", () => { + for (const width of [1, 2, 3, 7, 33, 100, 799, 800]) { + for (const height of [1, 2, 3, 7, 33, 100, 799, 1201]) { + for (const f of [0.001, 0.1, 1 / 3, 0.5, 0.667, 0.9, 0.999]) { + const got = at( + { x: 1 - f, y: 1 - f, width: f, height: f }, + width, + height, + ); + + expect(got.width).toBeGreaterThanOrEqual(1); + expect(got.height).toBeGreaterThanOrEqual(1); + expect(got.left + got.width).toBeLessThanOrEqual(width); + expect(got.top + got.height).toBeLessThanOrEqual(height); + } + } + } + }); +}); + +describe("snapWidth", () => { + // 3000px of canvas and a 2:3 frame, so a width of 1000 is the 1000 x 1500 + // that someone dragging a corner is trying to land on. + const CANVAS = 3000; + const px = (fraction: number, canvas = CANVAS) => + Math.round(fraction * canvas); + const snapped = (wanted: number, canvas = CANVAS) => + px(snapWidth(wanted / canvas, canvas, PORTRAIT), canvas); + + it("pulls a near miss onto the round number", () => { + expect(snapped(1012)).toBe(1000); + expect(snapped(988)).toBe(1000); + }); + + // The height is not free: a locked frame is the template's shape, so the + // width is the only thing to choose and the height follows from it. + it("lands both dimensions on round numbers together", () => { + const width = snapped(1012); + expect(Math.round(width / PORTRAIT)).toBe(1500); + expect(isRoundSize(width, Math.round(width / PORTRAIT))).toBe(true); + }); + + // 1000 x 1500 is what was being aimed at; 1025 x 1538 merely also has a + // round width. The coarser candidate has to win where both are in reach. + it("prefers the roundest pair within reach", () => { + expect(snapped(1020)).toBe(1000); + }); + + // A pair is only as round as its uglier half. On a 16:9 template a width of + // 1000 is rounder than 1600, but it produces a height of 563 where 1600 + // produces 900 -- so scoring the two together has to prefer the second, and + // scoring the width alone gets it exactly backwards. + it("judges the pair, not the width", () => { + const wide = (wanted: number) => + px(snapWidth(wanted / CANVAS, CANVAS, LANDSCAPE)); + + expect(wide(1010)).not.toBe(1000); + expect(wide(1610)).toBe(1600); + expect(Math.round(1600 / LANDSCAPE)).toBe(900); + }); + + // The ladder goes down to fives, so there is always something tidy nearby + // and no dead zone where the readout goes back to being arbitrary. What the + // tolerance governs is how far the frame may be pulled to reach it -- past + // that the frame is not nearly right, it is somewhere else, and moving it + // would be the tool overruling the person. + it("never pulls a frame further than the tolerance", () => { + for (let wanted = 900; wanted <= 1100; wanted += 1) { + expect(Math.abs(snapped(wanted) - wanted)).toBeLessThanOrEqual( + Math.max(4, wanted * 0.02), + ); + } + }); + + it("does not reach a distant round number", () => { + expect(snapped(1043)).not.toBe(1000); + expect(snapped(1043)).not.toBe(1100); + }); + + it("never leaves the canvas", () => { + for (const fraction of [0.5, 0.9, 0.99, 1]) { + expect(snapWidth(fraction, CANVAS, PORTRAIT)).toBeLessThanOrEqual(1); + } + }); + + // A snap that silently stopped working on smaller sources would be worse + // than none, since those are exactly the uploads whose size is marginal. + it("still finds something to land on when the image is small", () => { + expect(snapped(102, 300)).toBe(100); + }); +}); + +describe("isRoundSize", () => { + it("accepts a pair that was aimed at", () => { + expect(isRoundSize(1000, 1500)).toBe(true); + }); + + it("rejects a pair that merely happened", () => { + expect(isRoundSize(1012, 1518)).toBe(false); + // Round on one axis only is not a size anybody chose. + expect(isRoundSize(1010, 1515)).toBe(false); + }); +}); + +describe("resizeRect snapping", () => { + // A 3000x4500 canvas with a 2:3 frame: the fractions and the pixels line up + // simply enough that the snap is visible in the result. + const CANVAS = 3000; + const outputWidth = (rect: CropRect) => Math.round(rect.width * CANVAS); + + it("snaps the size when it knows the canvas", () => { + const resized = resizeRect({ + rect: FULL_FRAME, + handle: "se", + dx: -0.66, + dy: 0, + targetAspect: PORTRAIT, + imageAspect: PORTRAIT, + canvasWidth: CANVAS, + }); + expect(outputWidth(resized)).toBe(1000); + }); + + // The same drag with nothing to measure against has to be left alone, or the + // snap would be happening on some other unit than pixels. + it("leaves the size alone when it does not", () => { + const resized = resizeRect({ + rect: FULL_FRAME, + handle: "se", + dx: -0.66, + dy: 0, + targetAspect: PORTRAIT, + imageAspect: PORTRAIT, + }); + expect(outputWidth(resized)).toBe(1020); + }); + + // Shift-resizing goes down an entirely separate path, and a snap wired into + // only one of them would work until someone held Shift. + it("snaps while holding a guide too", () => { + const resized = resizeRect({ + rect: FULL_FRAME, + handle: "se", + dx: -0.66, + dy: 0, + targetAspect: PORTRAIT, + imageAspect: PORTRAIT, + hold: { y: 0.425 }, + canvasWidth: CANVAS, + }); + expect(outputWidth(resized)).toBe(1000); + }); + + // Running out of image is not negotiable and a round number is. + it("gives up the round number rather than leave the image", () => { + const resized = resizeRect({ + rect: { x: 0.9, y: 0, width: 0.1, height: 0.1, angle: 0 }, + handle: "se", + dx: 0.5, + dy: 0, + targetAspect: PORTRAIT, + imageAspect: PORTRAIT, + canvasWidth: CANVAS, + }); + expect(isInside(resized)).toBe(true); + }); +}); diff --git a/frontend/src/components/cropFrame/__tests__/holds.test.ts b/frontend/src/components/cropFrame/__tests__/holds.test.ts new file mode 100644 index 000000000..c5d25076c --- /dev/null +++ b/frontend/src/components/cropFrame/__tests__/holds.test.ts @@ -0,0 +1,134 @@ +import { + type CropGuide, + CropGuideAxisEnum, + CropGuideRoleEnum, +} from "src/graphql"; +import { describe, expect, it } from "vitest"; + +import { holdPointsFor } from "../holds"; + +const guide = ( + axis: CropGuideAxisEnum, + position: number, + role: CropGuideRoleEnum | null, + pivot = false, +): CropGuide => ({ + __typename: "CropGuide" as const, + axis, + position, + role, + label: null, + pivot, +}); + +const y = (position: number, role: CropGuideRoleEnum | null, pivot = false) => + guide(CropGuideAxisEnum.Y, position, role, pivot); +const x = (position: number, role: CropGuideRoleEnum | null, pivot = false) => + guide(CropGuideAxisEnum.X, position, role, pivot); + +/** + * The template says which line, and nothing here works it out. + * + * This used to take the anchor nearest the middle, on the reasoning that an + * interior anchor is what a subject gets lined up on while the ones at the + * extremes are framing limits. The reasoning was sound; the guess was still + * wrong for two of the seven shipped templates. The cases below are the ones + * that reasoning got right and the ones it did not, kept together so the + * difference is legible + */ +describe("holdPointsFor", () => { + it("holds the line the template names", () => { + const held = holdPointsFor([ + y(0.025, CropGuideRoleEnum.ANCHOR), // top of the head + y(0.425, CropGuideRoleEnum.REFERENCE, true), // bisects the eyes + y(0.77, CropGuideRoleEnum.ANCHOR), // bottom of the chin + ]); + expect(held.y).toBeCloseTo(0.425); + }); + + // The reason pivot is not another role value. A headshot's eye line is the + // softest line in its template - the head and chin are the hard limits -- + // and is still the right thing to resize about, so no rule reading role can + // reach it + it("holds a soft line when that is what the template names", () => { + const held = holdPointsFor([ + y(0.025, CropGuideRoleEnum.ANCHOR), + y(0.425, CropGuideRoleEnum.REFERENCE, true), + ]); + expect(held.y).toBeCloseTo(0.425); + }); + + // One rule, three real templates that once broke it: without a named pivot + // the axis centres, whatever else the template draws there. + for (const [name, guides] of [ + [ + // CROP_FULL_BODY: the head and the feet, both framing limits, + // equidistant from the middle. The old rule broke the tie by slice order + // and resized about the top of the head -- the very edge being dragged. + "two framing limits and no line named (CROP_FULL_BODY)", + [y(0.01, CropGuideRoleEnum.ANCHOR), y(0.99, CropGuideRoleEnum.ANCHOR)], + ], + [ + // CROP_TORSO: one anchor, at the top of the hair. Being the only one + // did not make it the right one. + "a lone anchor (CROP_TORSO)", + [ + y(0.01, CropGuideRoleEnum.ANCHOR), + y(1 / 3, CropGuideRoleEnum.REFERENCE), + y(2 / 3, CropGuideRoleEnum.REFERENCE), + ], + ], + [ + // CROP_WIDE: margins and thirds and nothing to line a body up on, so + // Shift becomes "resize about the middle" -- what every other editor + // does with the modifier. + "an axis the template says nothing about (CROP_WIDE)", + [ + y(0.01, CropGuideRoleEnum.MARGIN), + x(1 / 3, CropGuideRoleEnum.REFERENCE), + ], + ], + ] as const) { + it(`centres on ${name}`, () => { + const held = holdPointsFor([...guides]); + expect(held.y).toBeCloseTo(0.5); + expect(held.x).toBeCloseTo(0.5); + }); + } + + it("centres with no guides at all", () => { + expect(holdPointsFor([])).toEqual({ x: 0.5, y: 0.5 }); + }); + + it("treats the axes separately", () => { + const held = holdPointsFor([ + y(0.425, CropGuideRoleEnum.REFERENCE, true), + x(0.2, CropGuideRoleEnum.REFERENCE, true), + ]); + expect(held.y).toBeCloseTo(0.425); + expect(held.x).toBeCloseTo(0.2); + }); + + // A pivot on one axis says nothing about the other, and must not be borrowed + // across. + it("does not let one axis answer for the other", () => { + const held = holdPointsFor([ + y(0.425, CropGuideRoleEnum.REFERENCE, true), + x(0.2, CropGuideRoleEnum.ANCHOR), + ]); + expect(held.y).toBeCloseTo(0.425); + expect(held.x).toBeCloseTo(0.5); + }); + + // The reader drops both pivots when a template claims two on an axis, so + // this should not arrive. If it does, the first is as arbitrary as the + // second but it must still be a number, not undefined. + it("returns a usable point even if two lines claim the axis", () => { + const held = holdPointsFor([ + y(0.3, CropGuideRoleEnum.REFERENCE, true), + y(0.6, CropGuideRoleEnum.REFERENCE, true), + ]); + expect(held.y).toBeGreaterThan(0); + expect(held.y).toBeLessThan(1); + }); +}); diff --git a/frontend/src/components/cropFrame/__tests__/shapePath.test.ts b/frontend/src/components/cropFrame/__tests__/shapePath.test.ts new file mode 100644 index 000000000..abebb3b8c --- /dev/null +++ b/frontend/src/components/cropFrame/__tests__/shapePath.test.ts @@ -0,0 +1,102 @@ +import type { CropShape, CropSubpath } from "src/graphql"; +import { describe, expect, it } from "vitest"; + +import { shapePath } from "../shapePath"; + +const point = (x: number, y: number) => ({ + __typename: "CropPoint" as const, + x, + y, +}); + +/** A knot whose controls sit on its anchor, which is a straight corner. */ +const corner = (x: number, y: number) => ({ + __typename: "CropKnot" as const, + control_in: point(x, y), + anchor: point(x, y), + control_out: point(x, y), +}); + +const knot = ( + anchor: [number, number], + cin: [number, number], + cout: [number, number], +) => ({ + __typename: "CropKnot" as const, + control_in: point(...cin), + anchor: point(...anchor), + control_out: point(...cout), +}); + +const subpath = (knots: CropSubpath["knots"], closed = true): CropSubpath => ({ + __typename: "CropSubpath" as const, + closed, + knots, +}); + +const shape = (subpaths: CropSubpath[]): CropShape => ({ + __typename: "CropShape" as const, + label: null, + subpaths, +}); + +describe("shapePath", () => { + it("starts at the first anchor", () => { + const d = shapePath( + shape([subpath([corner(0.1, 0.2), corner(0.9, 0.2), corner(0.9, 0.8)])]), + ); + expect(d.startsWith("M0.1,0.2")).toBe(true); + }); + + // Photoshop has no straight segment: it draws one as a curve whose controls + // sit on its anchors. Writing every segment as a cubic is what lets a + // rectangle and an ellipse come through the same code. + it("writes every segment as a cubic", () => { + const d = shapePath( + shape([subpath([corner(0, 0), corner(1, 0), corner(1, 1)], false)]), + ); + expect(d.match(/C/g)).toHaveLength(2); + expect(d).not.toContain("L"); + }); + + // The segment back to the start is a curve like any other. A bare Z draws a + // straight line home, which would flatten one quarter of every ellipse + it("closes with a curve, not a straight line", () => { + const top = knot([0.5, 0.02], [0.28, 0.02], [0.72, 0.02]); + const right = knot([0.9, 0.4], [0.9, 0.19], [0.9, 0.6]); + const bottom = knot([0.5, 0.77], [0.72, 0.77], [0.28, 0.77]); + const left = knot([0.09, 0.4], [0.09, 0.6], [0.09, 0.19]); + + const d = shapePath(shape([subpath([top, right, bottom, left])])); + + // Four knots, four segments: three between them and one home again + expect(d.match(/C/g)).toHaveLength(4); + expect(d.endsWith("Z")).toBe(true); + // The closing curve carries the last knot's outgoing control and the + // first's incoming one, which is exactly what a bare Z would discard + expect(d).toContain("C0.09,0.19 0.28,0.02 0.5,0.02Z"); + }); + + it("leaves an open subpath open", () => { + const d = shapePath(shape([subpath([corner(0, 0), corner(1, 1)], false)])); + expect(d).not.toContain("Z"); + expect(d.match(/C/g)).toHaveLength(1); + }); + + it("joins a shape's subpaths into one path", () => { + const d = shapePath( + shape([ + subpath([corner(0, 0), corner(1, 0), corner(1, 1)]), + subpath([corner(0.2, 0.2), corner(0.8, 0.2), corner(0.8, 0.8)]), + ]), + ); + expect(d.match(/M/g)).toHaveLength(2); + }); + + // A stroke cap on a zero-length curve sits on the picture like a speck of + // dust on the lens + it("draws nothing for a subpath with no segment", () => { + expect(shapePath(shape([subpath([corner(0.5, 0.5)])]))).toBe(""); + expect(shapePath(shape([subpath([])]))).toBe(""); + }); +}); diff --git a/frontend/src/components/cropFrame/geometry.ts b/frontend/src/components/cropFrame/geometry.ts new file mode 100644 index 000000000..9b6dbf887 --- /dev/null +++ b/frontend/src/components/cropFrame/geometry.ts @@ -0,0 +1,451 @@ +/** + * The arithmetic behind the crop frame, kept apart from the pointer handling + * so it can be reasoned about and tested without a DOM. + * + * Everything is in fractions of the image, matching what the server accepts. + * That means a frame's aspect ratio is *not* `width / height` -- those are + * fractions of two different spans -- so the image's own proportions have to + * come into every calculation that locks a shape. + * + * What a resize must hold true, numbered so the tests can name what they are + * checking: + * + * I1. The frame stays inside the image. Anything else `crop.go` rejects. + * I2. Without a hold, the corner opposite the handle does not move. + * I3. With a target aspect, the shape is exact, not close. + * I4. Both axes stay at least MIN_SIZE, unless the image has run out -- + * I1 outranks this, since a frame that left the image to stay + * grabbable would not upload at all. + * I5. With a hold, the held line stays on the same part of the image. + * I6. All of the above still hold after a second drag from another corner. + */ + +export interface CropRect { + x: number; + y: number; + width: number; + height: number; + /** Degrees clockwise. The frame is measured against the rotated image. */ + angle: number; +} + +export const FULL_FRAME: CropRect = { + x: 0, + y: 0, + width: 1, + height: 1, + angle: 0, +}; + +const clamp = (value: number, low: number, high: number) => + Math.min(Math.max(value, low), high); + +/** + * The bounding box of a `width` x `height` rectangle turned by `angle`. + * + * Rotation grows the canvas rather than clipping the corners, so that + * straightening a horizon leaves the whole picture available to crop from. + * libvips does the same on the server, which is what keeps the frame the client + * drags and the frame the server cuts in agreement. + */ +export const rotatedSize = (width: number, height: number, angle: number) => { + const radians = (angle * Math.PI) / 180; + const sin = Math.abs(Math.sin(radians)); + const cos = Math.abs(Math.cos(radians)); + + return { + width: width * cos + height * sin, + height: width * sin + height * cos, + }; +}; + +/** + * How many pixels the crop will come out at. + * + * The same arithmetic as `CropRect.pixels` in internal/image/crop.go, clamps + * included, so the number shown is the number produced. Go rounds half away + * from zero where JavaScript rounds half upward; every value here is positive, + * so the two agree. + * + * Exact at an angle of zero. Turned, it is within a pixel or so: libvips grows + * the canvas to the rotated bounding box the same way, but rounds that box + * itself, and the fractions are then measured against the rounded result. + */ +export const cropPixels = ( + rect: CropRect, + naturalWidth: number, + naturalHeight: number, +) => { + const turned = rotatedSize(naturalWidth, naturalHeight, rect.angle); + const width = Math.round(turned.width); + const height = Math.round(turned.height); + + const left = clamp(Math.round(rect.x * width), 0, width - 1); + const top = clamp(Math.round(rect.y * height), 0, height - 1); + + // The offsets are returned as well as the size, though only the size is + // displayed. The clamps below exist to stop left + width reaching past the + // edge, and a result that hid the offsets could not be checked for it. + return { + left, + top, + width: clamp(Math.round(rect.width * width), 1, width - left), + height: clamp(Math.round(rect.height * height), 1, height - top), + }; +}; + +/** + * The fractional height that gives a frame of `targetAspect` on an image whose + * own proportions are `imageAspect`. + * + * A half-width, half-height frame on a 200x300 image is 100x150 pixels -- an + * aspect of 2:3, not 1:1. This is that conversion, and forgetting it is why a + * locked frame drifts out of shape as the image changes. + */ +export const heightForWidth = ( + width: number, + targetAspect: number, + imageAspect: number, +) => (width * imageAspect) / targetAspect; + +export const widthForHeight = ( + height: number, + targetAspect: number, + imageAspect: number, +) => (height * targetAspect) / imageAspect; + +/** + * The largest frame of `targetAspect` that fits, centred. + * + * Where a crop starts from: the most of the picture the chosen shape can hold, + * so a contributor adjusts rather than builds from nothing. + */ +export const largestCenteredRect = ( + targetAspect: number | undefined, + imageAspect: number, + angle = 0, +): CropRect => + // Refitting the whole frame is the same operation, exactly: it shrinks to the + // target shape about the frame's centre, and the whole frame's centre is the + // picture's. Named separately because the two are asked at different moments. + refitRect({ ...FULL_FRAME, angle }, targetAspect, imageAspect); + +/** + * Slide a frame, keeping it inside the image. + * + * The frame stops at the edge rather than shrinking. Someone dragging a frame + * that silently resized when it touched a border would have to start again, and + * the shape is usually the thing they care about most. + */ +export const moveRect = (rect: CropRect, dx: number, dy: number): CropRect => ({ + ...rect, + x: clamp(rect.x + dx, 0, 1 - rect.width), + y: clamp(rect.y + dy, 0, 1 - rect.height), +}); + +/** + * Output widths worth landing on. Largest first only so that a tie goes to the + * coarser candidate; the score below is what actually decides. + */ +const ROUND_STEPS = [1000, 500, 250, 200, 100, 50, 25, 10, 5]; + +/** The largest step a number is a multiple of, or 0 for none of them. */ +const roundness = (value: number) => { + for (const step of ROUND_STEPS) { + if (value % step === 0) return step; + } + return 0; +}; + +/** + * How far a frame may be pulled to land on a round size, as a fraction of its + * own width. + * + * Small on purpose. Snapping is meant to catch a frame that is nearly right, + * not to drag it somewhere it was not going: at two percent the pull is a few + * screen pixels at any sensible zoom, so it reads as the frame settling rather + * than as the frame disobeying. + */ +const SNAP_TOLERANCE = 0.02; + +/** + * Both dimensions round enough to look deliberate. + * + * Multiples of ten, which is the threshold below which a pair stops reading as + * a chosen size: 1000 x 1500 was aimed at, 1012 x 1518 merely happened. + */ +export const isRoundSize = (width: number, height: number) => + width % 10 === 0 && height % 10 === 0; + +/** + * Nudge a frame's width so the crop comes out at a round number of pixels. + * + * Only the width is chosen. The height is not free: a locked frame has the + * template's aspect, so the output is `width / targetAspect` whatever else + * happens, and the two cannot be rounded independently. + * + * Candidates are scored on how round *both* dimensions come out, so a 2:3 + * frame prefers 1000 x 1500 over 1025 x 1538 even though both widths are + * multiples of something. + */ +export const snapWidth = ( + width: number, + canvasWidth: number, + targetAspect: number, +) => { + const px = width * canvasWidth; + // A floor as well as a share, or a small frame can never reach the next + // round number and snapping quietly stops existing at low resolutions. + const limit = Math.max(4, px * SNAP_TOLERANCE); + + let best = width; + let bestScore = 0; + + for (const step of ROUND_STEPS) { + const candidate = Math.round(px / step) * step; + if (candidate < 1 || candidate > canvasWidth) continue; + if (Math.abs(candidate - px) > limit) continue; + + // The worse half, not the sum. A pair is only as round as its uglier + // dimension, and adding them lets a very round width outvote a height that + // is nothing of the kind -- on a 16:9 template that picks 1000 x 563 over + // 1600 x 900, which is exactly backwards. + const score = Math.min( + roundness(candidate), + roundness(Math.round(candidate / targetAspect)), + ); + if (score > bestScore) { + bestScore = score; + best = candidate / canvasWidth; + } + } + + return best; +}; + +export type Handle = "nw" | "ne" | "sw" | "se"; + +/** + * A point inside the frame to keep still while it is resized, per axis, as a + * fraction of the frame. + * + * A guide sits at a fraction of the frame, so a guide at `p` lands on the + * image at `y + p * h`. Resizing normally holds a corner and lets that drift; + * holding the guide instead means solving `y = held - p * h` as the height + * changes. Nothing else about the drag differs. + */ +export interface HoldPoints { + x?: number; + y?: number; +} + +/** + * The largest size that keeps `held` at fraction `p` of the frame while the + * frame stays inside the image. + * + * Two constraints, one for each edge: the near edge sits at `held - p * size` + * and must not go below 0, and the far edge sits at `held + (1 - p) * size` + * and must not pass 1. A guide at the very edge of the frame only constrains + * one of them. + */ +const maxSizeHolding = (held: number, p: number) => { + const before = p > 0 ? held / p : Number.POSITIVE_INFINITY; + const after = p < 1 ? (1 - held) / (1 - p) : Number.POSITIVE_INFINITY; + return Math.min(before, after); +}; + +/** The smallest a frame may be dragged, as a fraction. Below this it is too */ +/** small to grab hold of again. */ +export const MIN_SIZE = 0.02; + +/** + * How one axis behaves during a resize: what pins it, and how much room that + * pinning leaves. Anchoring a corner and holding a line differ only in these + * two answers, which is what lets a single resize serve both. + */ +interface Axis { + /** The largest the frame may be on this axis before it leaves the image. */ + room: number; + /** Where the frame starts on this axis, once its size there is settled. */ + place: (size: number) => number; +} + +/** Pinned to the edge opposite the handle, which the frame grows away from. */ +const anchoredAxis = ( + start: number, + span: number, + atFarEdge: boolean, +): Axis => { + const far = start + span; + return atFarEdge + ? { room: far, place: (size) => far - size } + : { room: 1 - start, place: () => start }; +}; + +/** Pinned to a line at fraction `p` of the frame, wherever it is on the image. */ +const heldAxis = (start: number, span: number, p: number): Axis => { + const held = start + p * span; + return { + room: Math.min(1, maxSizeHolding(held, p)), + place: (size) => held - p * size, + }; +}; + +/** + * Resize from a corner: ask, fit, place. + * + * The drag says what size it wants, the image says how much room there is, and + * only then is the frame put down. Deciding the size first is what keeps the + * pinned edge pinned (I2, I5) -- placing the frame and then trimming its size + * moves whatever it was pinned to. + * + * With `targetAspect` the axes are coupled, so the tighter one decides and the + * other follows (I3). With `hold` the frame grows around a line rather than + * away from a corner. + */ +export interface Resize { + rect: CropRect; + handle: Handle; + /** How far the pointer has moved since the press, in fractions of the image. */ + dx: number; + dy: number; + /** Width over height to lock to, or undefined to drag freely. */ + targetAspect?: number; + /** The image's own proportions, which every locked calculation needs. */ + imageAspect: number; + /** Lines to keep still, from the template. Absent means anchor a corner. */ + hold?: HoldPoints; + /** Pixels across, for snapping. Absent suspends it. */ + canvasWidth?: number; +} + +export const resizeRect = ({ + rect, + handle, + dx, + dy, + targetAspect, + imageAspect, + hold, + canvasWidth, +}: Resize): CropRect => { + const west = handle === "nw" || handle === "sw"; + const north = handle === "nw" || handle === "ne"; + + const axisX = + hold?.x !== undefined + ? heldAxis(rect.x, rect.width, hold.x) + : anchoredAxis(rect.x, rect.width, west); + const axisY = + hold?.y !== undefined + ? heldAxis(rect.y, rect.height, hold.y) + : anchoredAxis(rect.y, rect.height, north); + + // What the drag asks for, before the image gets a say. + let width = clamp(west ? rect.width - dx : rect.width + dx, MIN_SIZE, 1); + let height = clamp(north ? rect.height - dy : rect.height + dy, MIN_SIZE, 1); + + // Snapped here rather than at the end, so the shape follows from the round + // number instead of being rounded after the fact. + if (targetAspect !== undefined && canvasWidth !== undefined) { + width = snapWidth(width, canvasWidth, targetAspect); + } + + if (targetAspect === undefined) { + width = clamp(width, MIN_SIZE, Math.min(1, axisX.room)); + height = clamp(height, MIN_SIZE, Math.min(1, axisY.room)); + } else { + const room = Math.min( + 1, + axisX.room, + widthForHeight(Math.min(1, axisY.room), targetAspect, imageAspect), + ); + // Locked, the smallest usable frame is whichever of the two axes hits + // MIN_SIZE first. Flooring them separately is what pulled the shape apart. + const smallest = Math.max( + MIN_SIZE, + widthForHeight(MIN_SIZE, targetAspect, imageAspect), + ); + // Room outranks that floor: a frame that left the image to stay grabbable + // would be rejected outright by the server (I1 before I4). + width = clamp(width, Math.min(smallest, room), room); + height = heightForWidth(width, targetAspect, imageAspect); + } + + return { + ...rect, + width, + height, + x: axisX.place(width), + y: axisY.place(height), + }; +}; + +/** + * Put a frame back inside an image whose shape has changed. + * + * Rotating grows the canvas, so a frame that fitted before may not now, and a + * locked one is the wrong shape against the new proportions. The centre is + * kept, because that is where the subject is: a frame that jumped back to the + * middle every time the angle nudged would make straightening unusable. + */ +export const refitRect = ( + rect: CropRect, + targetAspect: number | undefined, + imageAspect: number, +): CropRect => { + const centreX = rect.x + rect.width / 2; + const centreY = rect.y + rect.height / 2; + + let width = clamp(rect.width, MIN_SIZE, 1); + let height = clamp(rect.height, MIN_SIZE, 1); + + if (targetAspect !== undefined) { + height = heightForWidth(width, targetAspect, imageAspect); + if (height > 1) { + height = 1; + width = widthForHeight(height, targetAspect, imageAspect); + } + } + + return { + ...rect, + width, + height, + x: clamp(centreX - width / 2, 0, 1 - width), + y: clamp(centreY - height / 2, 0, 1 - height), + }; +}; + +/** + * Whether an image's proportions match a template's. + * + * Compared against the template rather than against a fixed 2:3. + * + * The tolerance absorbs whole-pixel rounding: a 2:3 crop of an 800px-wide + * source is out by about a tenth of a percent, where an image that is actually + * the wrong shape is out by ten or more. Unusable numbers count as matching -- + * an SVG stores -1 for both, and badging it would say nothing true. + */ +export const matchesAspect = ( + width: number, + height: number, + targetAspect: number, + tolerance = 0.02, +) => { + if (!(width > 0) || !(height > 0) || !(targetAspect > 0)) return true; + return Math.abs(width / height - targetAspect) / targetAspect <= tolerance; +}; + +/** + * Whether a frame would leave the image exactly as it is. + * + * The same test the server makes before deciding whether to re-encode, so the + * form can offer to crop only when cropping would do something. + */ +export const isIdentity = (rect: CropRect) => + rect.angle === 0 && + rect.x === 0 && + rect.y === 0 && + rect.width === 1 && + rect.height === 1; diff --git a/frontend/src/components/cropFrame/holds.ts b/frontend/src/components/cropFrame/holds.ts new file mode 100644 index 000000000..778440a50 --- /dev/null +++ b/frontend/src/components/cropFrame/holds.ts @@ -0,0 +1,28 @@ +import { type CropGuide, CropGuideAxisEnum } from "src/graphql"; + +import type { HoldPoints } from "./geometry"; + +/** + * The point on each axis to hold still when resizing with Shift. + * + * The template says which line, and nothing here works it out. A guide carries + * `pivot` for exactly this, separately from `role`: in a headshot the eye line + * is the softest line in the template -- the head and the chin are the hard + * limits -- and is still the right thing to resize about. + * + * An axis with no pivot holds its centre, which is what every other editor does + * with the modifier. + * + * Only one point per axis can be held. Two would fix both the position and the + * size, leaving nothing for the drag to change -- which is why the reader drops + * both when a template claims two. + */ +export const holdPointsFor = (guides: CropGuide[]): HoldPoints => { + const pivotOn = (axis: CropGuideAxisEnum) => + guides.find((guide) => guide.axis === axis && guide.pivot)?.position ?? 0.5; + + return { + x: pivotOn(CropGuideAxisEnum.X), + y: pivotOn(CropGuideAxisEnum.Y), + }; +}; diff --git a/frontend/src/components/cropFrame/index.ts b/frontend/src/components/cropFrame/index.ts new file mode 100644 index 000000000..63fb97439 --- /dev/null +++ b/frontend/src/components/cropFrame/index.ts @@ -0,0 +1,14 @@ +import CropFrame from "./CropFrame"; +import CropOverlay from "./CropOverlay"; + +export default CropFrame; +export type { CropTemplateInfo } from "./CropOverlay"; +export type { CropRect } from "./geometry"; +export { + FULL_FRAME, + isIdentity, + largestCenteredRect, + matchesAspect, + rotatedSize, +} from "./geometry"; +export { CropOverlay }; diff --git a/frontend/src/components/cropFrame/shapePath.ts b/frontend/src/components/cropFrame/shapePath.ts new file mode 100644 index 000000000..1785860a5 --- /dev/null +++ b/frontend/src/components/cropFrame/shapePath.ts @@ -0,0 +1,88 @@ +import type { CropShape, CropSubpath } from "src/graphql"; + +/** + * Turning a template's outlines into SVG path data. + * + * Photoshop and SVG describe a curve the same way -- anchors with a control + * point either side -- so this is a transcription rather than a conversion, and + * every segment is a cubic even when it is visibly straight. A rectangle drawn + * with the shape tool arrives as four cubics whose controls sit on their + * anchors, and writing it out as one keeps rectangles and ellipses on the same + * path through here + */ + +/** + * Enough places for a canvas far larger than anything a template is drawn at, + * and few enough that the string stays readable. At five decimals a fraction + * resolves to a tenth of a pixel on a 10,000px image. + */ +const PLACES = 5; + +const round = (value: number) => Number(value.toFixed(PLACES)); + +const at = (point: { x: number; y: number }) => + `${round(point.x)},${round(point.y)}`; + +const subpathData = (subpath: CropSubpath) => { + const { knots } = subpath; + if (knots.length === 0) return ""; + + // A single knot is a point: it has no segment to draw, and emitting a + // zero-length curve leaves a stroke cap sitting on the image like a speck of + // dust on the lens + if (knots.length === 1) return ""; + + const parts = [`M${at(knots[0].anchor)}`]; + + for (let i = 1; i < knots.length; i++) { + parts.push( + `C${at(knots[i - 1].control_out)} ${at(knots[i].control_in)} ${at(knots[i].anchor)}`, + ); + } + + if (subpath.closed) { + // The closing segment is a curve like any other, so it has to be written + // out: a bare Z draws a straight line home and would flatten one quarter of + // every ellipse + const last = knots[knots.length - 1]; + const first = knots[0]; + parts.push( + `C${at(last.control_out)} ${at(first.control_in)} ${at(first.anchor)}`, + "Z", + ); + } + + return parts.join(""); +}; + +/** One shape's outlines as a single `d` attribute */ +export const shapePath = (shape: CropShape) => + shape.subpaths.map(subpathData).filter(Boolean).join(" "); + +/** + * The extent of a shape, for hanging a label on + * + * Anchors only. A control point can sit well outside the outline it governs -- + * on an ellipse they reach past every side - so including them would put the + * label somewhere the shape never goes + * + * Undefined for a shape with no knots, which has no position to speak of + */ +export const shapeBounds = (shape: CropShape) => { + let minX = Number.POSITIVE_INFINITY; + let minY = Number.POSITIVE_INFINITY; + let maxX = Number.NEGATIVE_INFINITY; + let maxY = Number.NEGATIVE_INFINITY; + + for (const subpath of shape.subpaths) { + for (const { anchor } of subpath.knots) { + minX = Math.min(minX, anchor.x); + maxX = Math.max(maxX, anchor.x); + minY = Math.min(minY, anchor.y); + maxY = Math.max(maxY, anchor.y); + } + } + + if (!Number.isFinite(minX)) return undefined; + return { minX, minY, maxX, maxY }; +}; diff --git a/frontend/src/components/cropFrame/styles.scss b/frontend/src/components/cropFrame/styles.scss new file mode 100644 index 000000000..7e0879209 --- /dev/null +++ b/frontend/src/components/cropFrame/styles.scss @@ -0,0 +1,293 @@ +.CropOverlay { + height: 100%; + left: 0; + pointer-events: none; + position: absolute; + top: 0; + width: 100%; + // Above .Image-image, which sits on z-index 1. Without this the guides are + // painted underneath the image they describe + z-index: 2; + + // Drawn over an image that does not fill the frame. The edge is the only + // thing saying where the crop would end, since the guides inside it stop + // short of the photograph and would otherwise look simply misplaced + &-inset { + box-shadow: 0 0 0 1px rgb(255 255 255 / 45%); + } + + // The outline layer fills the box the overlay is placed in. Fill is none and + // the stroke is deliberately quiet: a shape is where to put the subject, not + // something to look at instead of them + &-shapes { + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; + } + + &-shape { + fill: none; + stroke: rgb(255 220 100 / 80%); + stroke-dasharray: 4 3; + stroke-width: 1.5; + } + + // Guides are positioned in percentages rather than drawn into an SVG + // viewBox, so a line stays one pixel wide whatever shape the box is. A + // stretched viewBox gives horizontal and vertical strokes different weights + &-guide { + position: absolute; + + &-vertical { + border-left: 1px dashed rgb(255 255 255 / 55%); + height: 100%; + top: 0; + } + + &-horizontal { + border-top: 1px dashed rgb(255 255 255 / 55%); + left: 0; + width: 100%; + } + + // Anchors are meant to be hit, references only judged against, so the + // difference has to be visible without reading anything + &-anchor { + &.CropOverlay-guide-vertical { + border-left-style: solid; + border-left-width: 2px; + border-left-color: rgb(255 220 100 / 90%); + } + + &.CropOverlay-guide-horizontal { + border-top-style: solid; + border-top-width: 2px; + border-top-color: rgb(255 220 100 / 90%); + } + } + + &-margin { + &.CropOverlay-guide-vertical { + border-left-style: dotted; + } + + &.CropOverlay-guide-horizontal { + border-top-style: dotted; + } + } + + // The line the frame is currently being resized around + &-held { + &.CropOverlay-guide-vertical { + border-left: 2px solid rgb(120 220 255 / 95%); + } + + &.CropOverlay-guide-horizontal { + border-top: 2px solid rgb(120 220 255 / 95%); + } + } + } + + &-label { + background-color: rgb(0 0 0 / 65%); + border-radius: 2px; + color: rgb(255 220 100 / 95%); + font-size: 0.7rem; + line-height: 1.2; + padding: 0 3px; + position: absolute; + white-space: nowrap; + z-index: 1; + } + + &-shape-label { + transform: translate(-50%, -50%); + } + + &-guide-horizontal &-label { + margin-right: 0.4rem; + right: 100%; + top: 0; + transform: translateY(-50%); + } + + &-guide-vertical &-label { + bottom: 100%; + left: 0; + margin-bottom: 0.3rem; + transform: translateX(-50%); + } +} + +.CropFrame { + &-stage { + --stage-max-height: max(600px, 75vh); + + background-color: #000; + max-height: var(--stage-max-height); + margin: 0 auto; + position: relative; + user-select: none; + width: 100%; + } + + // Holds the picture and the dimming, and nothing else + &-clip { + inset: 0; + overflow: hidden; + position: absolute; + } + + &-shade { + box-shadow: 0 0 0 9999px rgb(0 0 0 / 55%); + pointer-events: none; + position: absolute; + } + + &-image { + left: 50%; + position: absolute; + top: 50%; + transform-origin: center; + } + + &-frame { + border: 1px solid rgb(255 255 255 / 90%); + cursor: move; + position: absolute; + + &:focus-visible { + outline: 2px solid $primary; + outline-offset: 2px; + } + + &-dragging { + cursor: grabbing; + } + } + + // Fills the frame so the whole of it can be grabbed, and sits under the + // corner handles so they win where they overlap + &-grip { + background: none; + border: 0; + cursor: move; + height: 100%; + left: 0; + padding: 0; + position: absolute; + top: 0; + width: 100%; + + &:focus-visible { + outline: 2px solid $primary; + outline-offset: 2px; + } + } + + &-handle { + z-index: 3; + background-color: #fff; + border: 1px solid rgb(0 0 0 / 50%); + border-radius: 2px; + height: 14px; + padding: 0; + position: absolute; + width: 14px; + + &-nw { + cursor: nwse-resize; + left: -7px; + top: -7px; + } + + &-ne { + cursor: nesw-resize; + right: -7px; + top: -7px; + } + + &-sw { + bottom: -7px; + cursor: nesw-resize; + left: -7px; + } + + &-se { + bottom: -7px; + cursor: nwse-resize; + right: -7px; + } + } + + &-status { + align-items: baseline; + display: flex; + gap: 1rem; + justify-content: space-between; + } + + &-size { + flex: none; + font-size: 0.75rem; + // The number changes continuously while a corner is dragged, and + // proportional digits make it jitter as it counts + font-variant-numeric: tabular-nums; + margin: 0.5rem 0 0; + opacity: 0.65; + white-space: nowrap; + + // Landed on a round size. Brightness only -- not weight, which tabular + // figures do not equalise across, and not a badge: the readout changes + // several times a second while a corner is dragged, and anything that + // alters its width would have it twitching the whole time + &-round { + opacity: 1; + } + } + + &-hint { + font-size: 0.75rem; + margin: 0.5rem 0 0; + opacity: 0.65; + // One line, whichever modifiers apply, so the image above does not move + // when a template with guides is chosen over one without. The readout + // beside it keeps its place; the hint is what gives way + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + kbd { + background-color: color-mix(in srgb, currentColor 18%, transparent); + border: 1px solid color-mix(in srgb, currentColor 35%, transparent); + border-radius: 3px; + box-shadow: none; + color: inherit; + font-size: inherit; + padding: 0 0.25rem; + } + } + + &-rotate { + margin-top: 1rem; + } + + &-rotate-head { + align-items: baseline; + display: flex; + justify-content: space-between; + } + + &-angle { + font-variant-numeric: tabular-nums; + padding: 0; + text-decoration: none; + + &:disabled { + color: inherit; + opacity: 0.6; + } + } +} diff --git a/frontend/src/components/dragList/DragList.tsx b/frontend/src/components/dragList/DragList.tsx new file mode 100644 index 000000000..4816544e3 --- /dev/null +++ b/frontend/src/components/dragList/DragList.tsx @@ -0,0 +1,147 @@ +import { faGripVertical } from "@fortawesome/free-solid-svg-icons"; +import cx from "classnames"; +import { + type DragEvent, + type KeyboardEvent, + type ReactNode, + useEffect, + useState, +} from "react"; +import { Icon } from "src/components/fragments"; + +const CLASSNAME = "DragList"; + +interface Props { + items: T[]; + /** Stable identity, used as the React key so a moved row keeps its DOM node. */ + keyOf: (item: T) => string; + /** Names the item in the handle's accessible label. */ + labelOf: (item: T) => string; + onReorder: (items: T[]) => void; + children: (item: T) => ReactNode; + /** Row treatment. Rows default to a single line; `is-block` hosts a card. */ + className?: string; +} + +// A list reordered by dragging a grip handle +// Rows are only draggable while the pointer is over their handle, otherwise +// they'd make nested controls impossible to use +// We also support keyboard navigation for accessibility +export function DragList({ + items, + keyOf, + labelOf, + onReorder, + children, + className, +}: Props) { + const [draft, setDraft] = useState(items); + const [dragIndex, setDragIndex] = useState(); + const [armedIndex, setArmedIndex] = useState(); + + useEffect(() => { + if (dragIndex === undefined) setDraft(items); + }, [items, dragIndex]); + + const reordered = (from: number, to: number): T[] | undefined => { + if (to < 0 || to >= draft.length || to === from) return undefined; + const next = [...draft]; + const [moved] = next.splice(from, 1); + next.splice(to, 0, moved); + return next; + }; + + // If dragIndex is set that means we're handling our own drag event + // and none of those belonging to any of our potential nested children + const owned = dragIndex !== undefined; + + const onDragStart = (event: DragEvent, index: number) => { + event.stopPropagation(); + event.dataTransfer.effectAllowed = "move"; + setDragIndex(index); + }; + + // Reordering as the cursor passes each row means the list previews the + // result so there is no separate drop indicator to keep in sync + const onDragEnter = (event: DragEvent, index: number) => { + if (!owned) return; + event.stopPropagation(); + + const next = reordered(dragIndex, index); + if (next) { + setDraft(next); + setDragIndex(index); + } + event.dataTransfer.dropEffect = "move"; + event.preventDefault(); + }; + + const onDrop = (event: DragEvent) => { + if (!owned) return; + event.stopPropagation(); + + setDragIndex(undefined); + setArmedIndex(undefined); + onReorder(draft); + }; + + const onKeyDown = ( + event: KeyboardEvent, + index: number, + ) => { + const step = + event.key === "ArrowUp" ? -1 : event.key === "ArrowDown" ? 1 : 0; + if (step === 0) return; + + const next = reordered(index, index + step); + event.preventDefault(); + if (!next) return; + + setDraft(next); + onReorder(next); + }; + + return ( +
    { + event.dataTransfer.dropEffect = "move"; + event.preventDefault(); + }} + > + {draft.map((item, index) => ( +
  • onDragStart(event, index)} + onDragEnter={(event) => onDragEnter(event, index)} + onDragEnd={(event) => { + if (!owned) return; + event.stopPropagation(); + setDragIndex(undefined); + setArmedIndex(undefined); + }} + onDrop={onDrop} + > + +
    {children(item)}
    +
  • + ))} +
+ ); +} diff --git a/frontend/src/components/dragList/__tests__/DragList.test.tsx b/frontend/src/components/dragList/__tests__/DragList.test.tsx new file mode 100644 index 000000000..ff5313b2d --- /dev/null +++ b/frontend/src/components/dragList/__tests__/DragList.test.tsx @@ -0,0 +1,197 @@ +import { fireEvent, screen } from "@testing-library/react"; +import { renderForm } from "src/test/renderForm"; +import { describe, expect, it, vi } from "vitest"; +import { DragList } from "../DragList"; + +const ITEMS = [ + { key: "a", name: "Alpha" }, + { key: "b", name: "Bravo" }, + { key: "c", name: "Charlie" }, +]; + +const setup = (onReorder = vi.fn()) => { + const utils = renderForm( + item.key} + labelOf={(item) => item.name} + onReorder={onReorder} + > + {(item) => {item.name}} + , + ); + return { ...utils, onReorder }; +}; + +const order = () => + [...document.querySelectorAll(".DragList-content")].map( + (el) => el.textContent, + ); + +const handles = () => screen.getAllByRole("button"); +const rows = () => [...document.querySelectorAll(".DragList-row")]; + +// jsdom does not implement DataTransfer, and React reads dropEffect off it +const dataTransfer = () => ({ effectAllowed: "", dropEffect: "" }); + +// A list of lists should be a valid setup +const nested = (outer: () => void, inner: () => void) => { + renderForm( + item.key} + labelOf={(item) => item.name} + onReorder={outer} + > + {(item) => ( + child.key} + labelOf={(child) => child.name} + onReorder={inner} + > + {(child) => {child.name}} + + )} + , + ); + + const outerRows = () => [ + ...document.querySelectorAll( + ".DragList.is-block > .DragList-row", + ), + ]; + return { + outerRows, + innerRowsOf: (index: number) => [ + ...outerRows()[index].querySelectorAll( + ".DragList-row .DragList-row", + ), + ], + }; +}; + +describe("DragList", () => { + describe("keyboard", () => { + it("moves an item down and reports the new order", async () => { + const { user, onReorder } = setup(); + + handles()[0].focus(); + await user.keyboard("{ArrowDown}"); + + expect(order()).toEqual(["Bravo", "Alpha", "Charlie"]); + expect(onReorder).toHaveBeenCalledWith([ITEMS[1], ITEMS[0], ITEMS[2]]); + }); + + it("moves an item up", async () => { + const { user, onReorder } = setup(); + + handles()[2].focus(); + await user.keyboard("{ArrowUp}"); + + expect(order()).toEqual(["Alpha", "Charlie", "Bravo"]); + expect(onReorder).toHaveBeenCalledWith([ITEMS[0], ITEMS[2], ITEMS[1]]); + }); + + it("does nothing at the ends", async () => { + const { user, onReorder } = setup(); + + handles()[0].focus(); + await user.keyboard("{ArrowUp}"); + handles()[2].focus(); + await user.keyboard("{ArrowDown}"); + + expect(order()).toEqual(["Alpha", "Bravo", "Charlie"]); + expect(onReorder).not.toHaveBeenCalled(); + }); + + it("keeps focus on the item that moved, so it can be moved again", async () => { + const { user } = setup(); + + handles()[0].focus(); + await user.keyboard("{ArrowDown}{ArrowDown}"); + + expect(order()).toEqual(["Bravo", "Charlie", "Alpha"]); + }); + }); + + describe("dragging", () => { + it("arms a row only while its handle is hovered", async () => { + const { user } = setup(); + + expect(rows().map((row) => row.getAttribute("draggable"))).toEqual([ + "false", + "false", + "false", + ]); + + await user.hover(handles()[1]); + expect(rows()[1]).toHaveAttribute("draggable", "true"); + expect(rows()[0]).toHaveAttribute("draggable", "false"); + + await user.unhover(handles()[1]); + expect(rows()[1]).toHaveAttribute("draggable", "false"); + }); + + it("previews the reorder while dragging and commits on drop", () => { + const { onReorder } = setup(); + + fireEvent.dragStart(rows()[0], { dataTransfer: dataTransfer() }); + fireEvent.dragEnter(rows()[2], { dataTransfer: dataTransfer() }); + + // Moved in the list before the drop, so the list previews the result + expect(order()).toEqual(["Bravo", "Charlie", "Alpha"]); + expect(onReorder).not.toHaveBeenCalled(); + + fireEvent.drop(rows()[2], { dataTransfer: dataTransfer() }); + expect(onReorder).toHaveBeenCalledWith([ITEMS[1], ITEMS[2], ITEMS[0]]); + }); + + it("does not disturb an enclosing list", () => { + const outer = vi.fn(); + const inner = vi.fn(); + const { outerRows, innerRowsOf } = nested(outer, inner); + + fireEvent.dragStart(innerRowsOf(0)[0], { dataTransfer: dataTransfer() }); + fireEvent.dragEnter(innerRowsOf(0)[1], { dataTransfer: dataTransfer() }); + fireEvent.drop(innerRowsOf(0)[1], { dataTransfer: dataTransfer() }); + + expect(inner).toHaveBeenCalledTimes(1); + expect(outer).not.toHaveBeenCalled(); + + // The outer list still holds its original order + expect( + outerRows().map( + (row) => row.querySelector(".DragList-handle")?.ariaLabel, + ), + ).toEqual(["Reorder Alpha", "Reorder Bravo", "Reorder Charlie"]); + }); + + it("lets an enclosing list reorder over its rows", () => { + const outer = vi.fn(); + const inner = vi.fn(); + const { outerRows, innerRowsOf } = nested(outer, inner); + + fireEvent.dragStart(outerRows()[0], { dataTransfer: dataTransfer() }); + // Enter a row of the *inner* list belonging to the second group. + fireEvent.dragEnter(innerRowsOf(1)[0], { dataTransfer: dataTransfer() }); + fireEvent.drop(innerRowsOf(1)[0], { dataTransfer: dataTransfer() }); + + expect(outer).toHaveBeenCalledWith([ITEMS[1], ITEMS[0], ITEMS[2]]); + expect(inner).not.toHaveBeenCalled(); + }); + + it("ignores drag movement that never started on a row", () => { + const { onReorder } = setup(); + + fireEvent.dragEnter(rows()[2], { dataTransfer: dataTransfer() }); + + expect(order()).toEqual(["Alpha", "Bravo", "Charlie"]); + expect(onReorder).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/frontend/src/components/dragList/index.ts b/frontend/src/components/dragList/index.ts new file mode 100644 index 000000000..f051c40f2 --- /dev/null +++ b/frontend/src/components/dragList/index.ts @@ -0,0 +1 @@ +export { DragList } from "./DragList"; diff --git a/frontend/src/components/dragList/styles.scss b/frontend/src/components/dragList/styles.scss new file mode 100644 index 000000000..5b4e19ac1 --- /dev/null +++ b/frontend/src/components/dragList/styles.scss @@ -0,0 +1,68 @@ +.DragList { + list-style: none; + margin: 0; + padding: 0; + + &-row { + align-items: stretch; + background-color: $textfield-bg; + border: 1px solid transparent; + border-radius: 3px; + display: flex; + margin-bottom: 0.25rem; + + &.is-dragging { + border-color: $primary; + opacity: 0.6; + } + } + + &-handle { + align-items: center; + background: none; + border: 0; + border-radius: 3px 0 0 3px; + color: $muted-gray; + cursor: grab; + display: flex; + flex-shrink: 0; + justify-content: center; + padding: 0; + width: 2rem; + + &:hover, + &:focus-visible { + background-color: rgba(138 155 168 / 15%); + color: $text-color; + } + + &:active { + cursor: grabbing; + } + } + + &-content { + align-items: center; + display: flex; + flex: 1 1 auto; + gap: 0.5rem; + min-width: 0; + padding: 0.4rem 0.75rem; + } + + // Rows that hold a whole card don't get a full-length + // header because that's quite noisy + &.is-block > .DragList-row { + background: none; + + > .DragList-handle { + align-items: flex-start; + padding-top: 0.9rem; + } + + > .DragList-content { + display: block; + padding: 0; + } + } +} diff --git a/frontend/src/components/editCard/ModifyEdit.tsx b/frontend/src/components/editCard/ModifyEdit.tsx index d24a1db9b..67b3e1898 100644 --- a/frontend/src/components/editCard/ModifyEdit.tsx +++ b/frontend/src/components/editCard/ModifyEdit.tsx @@ -3,7 +3,10 @@ import type { FC } from "react"; import { Col, Row } from "react-bootstrap"; import ChangeRow from "src/components/changeRow"; import { Icon } from "src/components/fragments"; -import ImageChangeRow from "src/components/imageChangeRow"; +import ImageChangeRow, { + type ImageAssignmentChange, + type ResultingImage, +} from "src/components/imageChangeRow"; import URLChangeRow, { type URL } from "src/components/urlChangeRow"; import { BreastTypes, @@ -53,7 +56,10 @@ export type Image = { type StartingWith = T extends `${K}${infer _}` ? T : never; export type TargetOldDetails = Omit< T, - StartingWith | "draft_id" + | StartingWith + | "draft_id" + | "image_changes" + | "typed_images" >; export interface TagDetails { @@ -138,6 +144,8 @@ export interface PerformerDetails { removed_images?: (Image | null)[] | null; added_urls?: URL[] | null; removed_urls?: URL[] | null; + image_changes?: ImageAssignmentChange[] | null; + typed_images?: ResultingImage[] | null; draft_id?: string | null; } @@ -337,6 +345,8 @@ export const renderPerformerDetails = ( {performerDetails.draft_id && ( diff --git a/frontend/src/components/editCard/__tests__/renderPerformerDetails.test.tsx b/frontend/src/components/editCard/__tests__/renderPerformerDetails.test.tsx index 47b9f7b5c..70c2bb014 100644 --- a/frontend/src/components/editCard/__tests__/renderPerformerDetails.test.tsx +++ b/frontend/src/components/editCard/__tests__/renderPerformerDetails.test.tsx @@ -1,11 +1,16 @@ -import { screen, within } from "@testing-library/react"; +import { screen, waitFor, within } from "@testing-library/react"; import { BreastTypeEnum, + CropGuideAxisEnum, + CropGuideRoleEnum, EthnicityEnum, EyeColorEnum, GenderEnum, HairColorEnum, + ImageTypeEnum, + ImageTypeGroupEnum, } from "src/graphql"; +import ImageTypeGroupsGQL from "src/graphql/queries/ImageTypeGroups.gql"; import { renderForm } from "src/test/renderForm"; import { describe, expect, it } from "vitest"; import { @@ -309,3 +314,280 @@ describe("renderPerformerDetails", () => { }); }); }); + +describe("image label changes", () => { + const image = (id: string) => ({ + id, + url: `url-${id}`, + width: 400, + height: 600, + }); + + it("states each image once with its own added, removed and date changes", () => { + render( + { + image_changes: [ + { + image: image("img-1"), + added_types: ["SHOT_PORTRAIT", "CROP_FACE"], + removed_types: ["CROP_WIDE"], + date: "2019-06", + date_changed: true, + }, + { + image: image("img-2"), + added_types: [], + removed_types: ["DRESS_NUDE"], + date: null, + date_changed: true, + }, + ], + }, + undefined, + true, + ); + + const row = rowFor("Images"); + + // One cell per image, not one per label + expect(row.querySelectorAll(".ImageChangeRow-chips")).toHaveLength(2); + + // Falls back to the raw keys here since the vocabulary query is not mocked out + expect(within(row).getByText(/SHOT_PORTRAIT/)).toBeInTheDocument(); + expect(within(row).getByText(/CROP_FACE/)).toBeInTheDocument(); + expect(within(row).getByText(/CROP_WIDE/)).toBeInTheDocument(); + expect(within(row).getByText(/DRESS_NUDE/)).toBeInTheDocument(); + + expect(within(row).getByText("Date 2019-06")).toBeInTheDocument(); + expect(within(row).getByText("Date cleared")).toBeInTheDocument(); + + // In real usage these would hold descriptions of the labels, but since we haven't mocked the vocab + // call they should at least render blank instead of using the raw enum names + for (const chip of row.querySelectorAll(".badge")) { + expect(chip.getAttribute("title") ?? "").not.toMatch(/^[A-Z_]+$/); + } + }); + + it("shows an added image once, with its labels attached", () => { + const added = image("img-new"); + render( + { + added_images: [added], + image_changes: [ + { + image: added, + added_types: ["SHOT_PORTRAIT"], + removed_types: [], + date: null, + date_changed: false, + }, + ], + }, + undefined, + true, + ); + + const row = rowFor("Images"); + + expect(row.querySelectorAll(".ImageChangeRow-image")).toHaveLength(1); + expect(within(row).getByText(/SHOT_PORTRAIT/)).toBeInTheDocument(); + // Grouped as added, and not also listed as merely relabelled. + expect(within(row).getByText("Added")).toBeInTheDocument(); + expect(within(row).queryByText("Relabelled")).toBeNull(); + }); + + // Belongs to neither column of a Removed / Added diff, which is what the + // separate row existed to carry. + it("groups a kept-but-relabelled image on its own", () => { + render( + { + image_changes: [ + { + image: image("img-kept"), + added_types: ["CROP_FACE"], + removed_types: [], + date: null, + date_changed: false, + }, + ], + }, + undefined, + true, + ); + + const row = rowFor("Images"); + expect(within(row).getByText("Relabelled")).toBeInTheDocument(); + expect(within(row).queryByText("Added")).toBeNull(); + }); + + it("renders nothing when nothing about the images changed", () => { + render({ image_changes: [] }, undefined, true); + expect(screen.queryByText("Images")).not.toBeInTheDocument(); + }); + + it("gives every cell its own image's aspect ratio", () => { + const wide = { id: "img-wide", url: "u-wide", width: 1600, height: 900 }; + const tall = { id: "img-tall", url: "u-tall", width: 400, height: 600 }; + render( + { + added_images: [wide, tall], + image_changes: [ + { + image: wide, + added_types: ["SHOT_CANDID"], + removed_types: [], + date: null, + date_changed: false, + }, + ], + }, + undefined, + true, + ); + + const row = rowFor("Images"); + const frames = row.querySelectorAll("button.Image"); + expect(frames).toHaveLength(2); + expect(frames[0].style.aspectRatio).toBe("1600/900"); + expect(frames[1].style.aspectRatio).toBe("400/600"); + }); +}); + +/** + * The lightbox opened from a diff is the one the edit form opens, minus the + * controls: a reviewer sees the labels an edit claims and can hold the picture + * against the frame it says it follows + */ +describe("the lightbox opened from an edit diff", () => { + const image = (id: string) => ({ + id, + url: `url-${id}`, + width: 400, + height: 600, + }); + + const vocabulary = { + request: { + query: ImageTypeGroupsGQL, + variables: {}, + }, + maxUsageCount: Number.POSITIVE_INFINITY, + result: { + data: { + imageTypeGroups: [ + { + __typename: "ImageTypeGroup" as const, + key: ImageTypeGroupEnum.CROP, + name: "Crop", + description: null, + exclusive: true, + enabled: true, + types: [ + { + __typename: "ImageType" as const, + key: ImageTypeEnum.CROP_FACE, + name: "Face", + description: null, + enabled: true, + conflicts_with: [], + crop_template: { + __typename: "CropTemplate" as const, + aspect_ratio: 2 / 3, + guides: [ + { + __typename: "CropGuide" as const, + axis: CropGuideAxisEnum.Y, + position: 0.397, + role: CropGuideRoleEnum.REFERENCE, + label: "Bisects the eyes", + pivot: true, + }, + ], + shapes: [], + }, + }, + ], + }, + ], + }, + }, + }; + + const added = image("img-new"); + + const openLightbox = async () => { + const { user } = renderForm( +
+ {renderPerformerDetails( + { + added_images: [added], + image_changes: [ + { + image: added, + added_types: [ImageTypeEnum.CROP_FACE], + removed_types: [], + date: null, + date_changed: false, + }, + ], + typed_images: [ + { + image: added, + types: [ImageTypeEnum.CROP_FACE], + date: null, + }, + ], + }, + undefined, + true, + )} +
, + { mocks: [vocabulary] }, + ); + + await waitFor(() => + expect( + document.querySelector(".ImageChangeRow-image button"), + ).toBeInTheDocument(), + ); + await user.click( + document.querySelector(".ImageChangeRow-image button") as HTMLElement, + ); + return user; + }; + + // The resulting labels, resolved to their names: this is the state being voted on + // rather than the "+ CROP_FACE" delta shown in the row behind it + it("names the labels the image ends up with", async () => { + await openLightbox(); + + const modal = document.querySelector(".modal") as HTMLElement; + await waitFor(() => + expect(within(modal).getByText("Face")).toBeInTheDocument(), + ); + }); + + // The frame the image claims. Without the template the toggle has nothing to + // draw and does not appear, so its presence is the wiring working + it("offers the guides for an image that claims a crop template", async () => { + const user = await openLightbox(); + + const modal = document.querySelector(".modal") as HTMLElement; + const toggle = await waitFor(() => + within(modal).getByRole("button", { name: "Show guides" }), + ); + + await user.click(toggle); + expect(document.querySelector(".CropOverlay")).toBeInTheDocument(); + }); + + // The reviewer is not editing. The lightbox becomes an editor only when it + // is handed one, and a diff hands it nothing + it("offers no editing controls", async () => { + await openLightbox(); + + const modal = document.querySelector(".modal") as HTMLElement; + expect(within(modal).queryByLabelText("Add label")).toBeNull(); + expect(within(modal).queryByText("Remove")).toBeNull(); + }); +}); diff --git a/frontend/src/components/editImages/CropStep.tsx b/frontend/src/components/editImages/CropStep.tsx new file mode 100644 index 000000000..0e0f97888 --- /dev/null +++ b/frontend/src/components/editImages/CropStep.tsx @@ -0,0 +1,228 @@ +import cx from "classnames"; +import { + forwardRef, + useEffect, + useImperativeHandle, + useMemo, + useState, +} from "react"; +import CropFrame, { + type CropRect, + FULL_FRAME, + isIdentity, + largestCenteredRect, + rotatedSize, +} from "src/components/cropFrame"; +import { LoadingIndicator } from "src/components/fragments"; +import type { + ImageCropInput, + ImageTypeEnum, + ImageTypeGroupsQuery, +} from "src/graphql"; + +import ImageLabels from "./ImageLabels"; + +type ImageTypeGroup = ImageTypeGroupsQuery["imageTypeGroups"][number]; + +const CLASSNAME = "CropStep"; + +interface CropStepProps { + file: File; + groups: ImageTypeGroup[]; + onCropsChange?: (crops: boolean) => void; + onUpload: ( + crop: ImageCropInput | undefined, + types: ImageTypeEnum[], + imageDate: string | null, + ) => void; +} + +export interface CropStepHandle { + upload: () => void; + reset: () => void; +} + +/** + * The step between choosing a file and uploading it: pick the crop this image + * is meant to be, drag its frame over the picture, send both + * + * The frame and the label are chosen in one action, which is the point: a Crop + * value applied afterwards is a judgement about a photograph but the one applied + * here is a description of what was just done to it, and true by construction. + * + * Cropping is never required. Doing nothing leaves a plain "Upload", exactly + * as before any of this existed, because there are uploads no frame suits like a + * close-up of a tattoo or a more artistic image from social media + */ +const CropStep = forwardRef(function CropStep( + { file, groups, onCropsChange, onUpload }, + ref, +) { + const [src, setSrc] = useState(); + const [size, setSize] = useState<{ width: number; height: number }>(); + const [failed, setFailed] = useState(false); + const [rect, setRect] = useState(FULL_FRAME); + // Everything this image is being called, crop included. Shaped like a gallery + // image so the same control serves both + const [labels, setLabels] = useState<{ + types: ImageTypeEnum[]; + date?: string | null; + }>({ types: [], date: null }); + + useEffect(() => { + const url = URL.createObjectURL(file); + setSrc(url); + setFailed(false); + + let live = true; + createImageBitmap(file, { imageOrientation: "from-image" }) + .then((bitmap) => { + if (live) setSize({ width: bitmap.width, height: bitmap.height }); + bitmap.close(); + }) + .catch(() => { + if (live) setFailed(true); + }); + + return () => { + live = false; + URL.revokeObjectURL(url); + }; + }, [file]); + + // Any type the instance has a template for, rather than anything matching a + // name: an instance that added a crop of its own gets a frame for it, and one + // that removed a template stops being offered that frame + const templates = useMemo( + () => groups.flatMap((group) => group.types).filter((t) => t.crop_template), + [groups], + ); + + const selected = templates.find((t) => labels.types.includes(t.key)); + const aspectRatio = selected?.crop_template?.aspect_ratio; + const guides = selected?.crop_template?.guides ?? []; + const shapes = selected?.crop_template?.shapes ?? []; + + // Choosing a different crop means a different shape, so the frame starts + // again at the largest one that fits rather than being squeezed out of the + // old one + useEffect(() => { + if (!size) return; + setRect((previous) => { + const turned = rotatedSize(size.width, size.height, previous.angle); + return largestCenteredRect( + aspectRatio, + turned.width / turned.height, + previous.angle, + ); + }); + }, [aspectRatio, size]); + + // Whether cropping would do anything. Choosing a frame that happens to select + // the whole picture is not a crop, and the server would skip it too, so the + // form does not offer to + const crops = !isIdentity(rect); + + useEffect(() => { + onCropsChange?.(crops); + }, [crops, onCropsChange]); + + // Back to the untouched file: no frame, no rotation, nothing cropped. The + // other labels stay, since they describe the photograph rather than what was + // done to it here + const reset = () => { + setLabels((previous) => ({ + ...previous, + types: previous.types.filter( + (type) => !templates.some((template) => template.key === type), + ), + })); + setRect(FULL_FRAME); + }; + + const upload = () => + onUpload( + crops + ? { + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height, + angle: rect.angle, + } + : undefined, + // The crop type is a label whether or not the frame cut anything: an + // image already the right shape is still that kind of picture + labels.types, + labels.date ?? null, + ); + + useImperativeHandle(ref, () => ({ upload, reset })); + + return ( +
0, + })} + > + {src && size && !failed ? ( + <> + {templates.length > 0 && ( +

+ {selected ? ( + + {selected.name} + {selected.description ? ` -- ${selected.description}` : ""} + + ) : ( + + Pick a crop below to frame this image against its template. + + )} + + {selected && ( + + Download template + + )} +

+ )} + + + + ) : ( +
+ {failed ? ( + This file cannot be cropped. Upload it as it is. + ) : ( + + )} +
+ )} + + {groups.some((group) => group.types.length > 0) && ( +
+ +
+ )} +
+ ); +}); + +export default CropStep; diff --git a/frontend/src/components/editImages/ImageLabels.tsx b/frontend/src/components/editImages/ImageLabels.tsx new file mode 100644 index 000000000..6470b97da --- /dev/null +++ b/frontend/src/components/editImages/ImageLabels.tsx @@ -0,0 +1,182 @@ +import cx from "classnames"; +import { useMemo } from "react"; +import { Form } from "react-bootstrap"; +import Select, { type OnChangeValue } from "react-select"; + +import { TagLink } from "src/components/fragments"; +import type { ImageTypeEnum, ImageTypeGroupsQuery } from "src/graphql"; +import { maxImageDate, partialDateError } from "src/utils"; + +import type { TypedImage } from "./types"; + +type ImageTypeGroup = ImageTypeGroupsQuery["imageTypeGroups"][number]; +type ImageType = ImageTypeGroup["types"][number]; + +/** + * Everything this control touches. Stated structurally rather than as a + * TypedImage, so the upload form (which is labelling something that is not an + * image yet) can use the same control as the lightbox gallery + */ +type Labelling = Pick; + +interface ImageLabelsProps { + groups: ImageTypeGroup[]; + value: T; + onChange: (value: T) => void; +} + +const CLASSNAME = "EditImages-labels"; + +interface Option { + value: ImageTypeEnum; + label: string; + group: string; + sublabel: string; +} + +/** + * The labels an image carries, as chips, plus one control to add another + * + * Groups are exclusive so for example picking a type like front view + * will make this stop offering the side and back views + */ +const ImageLabels = ({ + groups, + value, + onChange, +}: ImageLabelsProps) => { + const { byKey, groupOf, rankOf, options } = useMemo(() => { + const byKey = new Map(); + const groupOf = new Map(); + const rankOf = new Map(); + const options: { label: string; options: Option[] }[] = []; + + // The order the groups are offered in can be changed by admins + for (const group of groups) { + for (const type of group.types) { + byKey.set(type.key, type); + groupOf.set(type.key, group.key); + rankOf.set(type.key, rankOf.size); + } + + // If a type or type group have been disabled we + // still allow it to stay on the image, but do not offer to add + options.push({ + label: group.name, + options: group.types + .filter((type) => group.enabled && type.enabled) + .map((type) => ({ + value: type.key, + label: type.name, + group: group.key, + sublabel: type.description ?? "", + })), + }); + } + + return { byKey, groupOf, rankOf, options }; + }, [groups]); + + // Groups are exclusive, do not offer to add the "Face" label + // if "Full body" label has already been added + const unavailable = useMemo(() => { + const blocked = new Set(); + for (const chosen of value.types) { + for (const conflict of byKey.get(chosen)?.conflicts_with ?? []) { + blocked.add(conflict); + } + } + return blocked; + }, [value.types, byKey]); + + const answeredGroups = useMemo( + () => new Set(value.types.map((type) => groupOf.get(type))), + [value.types, groupOf], + ); + + const available = options + .map((group) => ({ + ...group, + options: group.options.filter( + (option) => + !unavailable.has(option.value) && !answeredGroups.has(option.group), + ), + })) + .filter((group) => group.options.length > 0); + + const addType = (result: OnChangeValue) => { + if (!result) return; + onChange({ ...value, types: [...value.types, result.value] }); + }; + + const removeType = (key: ImageTypeEnum) => + onChange({ ...value, types: value.types.filter((type) => type !== key) }); + + const dateError = partialDateError(value.date, maxImageDate()); + + // Render labels in the server-defined order rather than the order in which they were added to the image + const chosen = value.types + .map((key) => byKey.get(key)) + .filter((type): type is ImageType => type !== undefined) + .sort( + (a, b) => + (rankOf.get(a.key) ?? Infinity) - (rankOf.get(b.key) ?? Infinity), + ); + + return ( +
+
+ {chosen.map((type) => ( + removeType(type.key)} + disabled + /> + ))} +
+ +
+ + + ), + }} + />, + ); + await user.click(screen.getByRole("button", { name: /3/ })); + return { user }; + }; + + it("renders the editor for the focused image only", async () => { + await openEditing(); + + expect(screen.getAllByTestId("editing")).toHaveLength(1); + expect(screen.getByTestId("editing")).toHaveTextContent("a"); + }); + + it("moves the editor to whichever image is focused", async () => { + const { user } = await openEditing(); + + const thumbs = document.querySelectorAll(".ImageLightbox-thumb"); + await user.click(thumbs[2] as HTMLElement); + + expect(screen.getByTestId("editing")).toHaveTextContent("c"); + }); + + it("leaves arrow keys to a focused field", async () => { + await openEditing(); + const field = screen.getByLabelText("Notes"); + + field.focus(); + fireEvent.keyDown(field, { key: "ArrowRight" }); + + expect(screen.getByText(/1\/3/)).toBeInTheDocument(); + expect(screen.getByTestId("editing")).toHaveTextContent("a"); + + fireEvent.keyDown(document, { key: "ArrowRight" }); + await waitFor(() => expect(screen.getByText(/2\/3/)).toBeInTheDocument()); + }); + + it("labels the thumbnails, and not the focused image", async () => { + await openEditing({ a: ["Face"], b: ["Bust"] }); + + const thumbLabels = [ + ...document.querySelectorAll(".ImageLightbox-thumb-labels"), + ].map((el) => el.textContent); + expect(thumbLabels).toEqual(["Face", "Bust", ""]); + + // The focused image shows controls instead of an overlay + expect( + document.querySelector(".ImageLightbox-main .ImageLightbox-labels"), + ).toBeNull(); + }); + + it("does not close when the editor is used", async () => { + const { user } = await openEditing(); + const dialog = screen.getByRole("dialog"); + + await user.click(screen.getByLabelText("Notes")); + await user.click( + document.querySelector(".ImageLightbox-editor") as HTMLElement, + ); + + expect(dialog).toBeInTheDocument(); + }); + + it("still closes on Escape from a focused field", async () => { + const { user } = await openEditing(); + const dialog = screen.getByRole("dialog"); + + screen.getByLabelText("Notes").focus(); + await user.keyboard("{Escape}"); + + await waitFor(() => expect(dialog).not.toBeInTheDocument()); + }); + }); + describe("closing", () => { it("close button calls onHide", async () => { const { user } = await openLightbox(ONE); diff --git a/frontend/src/components/image/__tests__/ImageLightboxGuides.test.tsx b/frontend/src/components/image/__tests__/ImageLightboxGuides.test.tsx new file mode 100644 index 000000000..073c7c5f9 --- /dev/null +++ b/frontend/src/components/image/__tests__/ImageLightboxGuides.test.tsx @@ -0,0 +1,418 @@ +import { screen } from "@testing-library/react"; +import type { CropTemplateInfo } from "src/components/cropFrame"; +import { + type CropGuide, + CropGuideAxisEnum, + CropGuideRoleEnum, +} from "src/graphql"; +import { renderForm } from "src/test/renderForm"; +import { describe, expect, it } from "vitest"; +import ImageLightbox from "../ImageLightbox"; + +const img = (id: string) => ({ + id, + url: `https://example.com/${id}.jpg`, + width: 200, + height: 300, +}); + +const guide = (position: number, label: string): CropGuide => ({ + __typename: "CropGuide" as const, + axis: CropGuideAxisEnum.Y, + position, + role: CropGuideRoleEnum.ANCHOR, + label, + pivot: false, +}); + +// A 2:3 template, matching the 200x300 images below. +const FACE = { + aspectRatio: 2 / 3, + guides: [guide(0.425, "Bisects the eyes"), guide(0.77, "Chin")], + shapes: [], +}; + +const setup = (cropTemplates?: Record) => + renderForm( + {}} + />, + ); + +// Queried from the document, not the render container: the lightbox is a +// modal and renders into a portal on document.body. Scoping to the container +// finds nothing, which makes every "draws no guides" assertion pass for the +// wrong reason. +const drawn = () => document.querySelectorAll(".CropOverlay-guide"); + +describe("ImageLightbox guides", () => { + // The point of showing these: an image already uploaded can be held against + // the frame it says it follows, which is what a reviewer needs in an edit + // diff and has no other way to check. + it("draws the focused image's guides once asked for", async () => { + const { user } = setup({ a: FACE }); + + // Off until asked: looking at the photograph is what the lightbox is for. + expect(drawn()).toHaveLength(0); + + await user.click(screen.getByRole("button", { name: "Show guides" })); + + expect(drawn()).toHaveLength(2); + expect(screen.getByText("Bisects the eyes")).toBeInTheDocument(); + }); + + it("draws nothing for an image with no crop type", () => { + setup({ b: FACE }); + expect(drawn()).toHaveLength(0); + }); + + it("draws nothing when no guides are supplied at all", () => { + setup(); + expect(drawn()).toHaveLength(0); + }); + + // Guides are the reason to look, but the picture underneath has to be + // lookable-at unobstructed too. + it("can be turned on and back off", async () => { + const { user } = setup({ a: FACE }); + + await user.click(screen.getByRole("button", { name: "Show guides" })); + expect(drawn()).toHaveLength(2); + + await user.click(screen.getByRole("button", { name: "Hide guides" })); + expect(drawn()).toHaveLength(0); + }); + + // No toggle where there is nothing to toggle, or every image in a gallery + // grows a control that does nothing. + it("offers no toggle for an image without guides", () => { + setup({ b: FACE }); + expect(screen.queryByRole("button", { name: "Hide guides" })).toBeNull(); + expect(screen.queryByRole("button", { name: "Show guides" })).toBeNull(); + }); + + // Each image carries its own frame, so stepping through a gallery has to + // follow the focus rather than keeping the first image's guides. + it("follows the focused image", async () => { + const { user } = renderForm( + {}} + />, + ); + + // The first image has no template, so there is nothing to switch on. + expect(screen.queryByRole("button", { name: /guides/i })).toBeNull(); + + await user.keyboard("{ArrowRight}"); + + await user.click(screen.getByRole("button", { name: "Show guides" })); + expect(drawn()).toHaveLength(2); + }); +}); + +describe("ImageLightbox off-aspect badge", () => { + const offFrame = () => screen.queryByText(/off frame/i); + + // Checked against the template the image claims, not against a fixed 2:3, so + // an instance with its own templates gets an answer about its own rules. + it("says nothing when the image matches its template", () => { + setup({ a: FACE }); + expect(offFrame()).toBeNull(); + }); + + it("flags an image whose proportions are not the template's", () => { + renderForm( + {}} + />, + ); + expect(offFrame()).not.toBeNull(); + }); + + // Whole-pixel rounding puts a real crop a fraction off its template, and + // badging every correctly cropped image would make the badge worthless. + it("tolerates rounding", () => { + renderForm( + {}} + />, + ); + expect(offFrame()).toBeNull(); + }); + + it("says nothing about an image claiming no template", () => { + renderForm( + {}} + />, + ); + expect(offFrame()).toBeNull(); + }); + + // An SVG stores -1 for both dimensions. Badging it would say nothing true. + it("says nothing about an image with no real dimensions", () => { + renderForm( + {}} + />, + ); + expect(offFrame()).toBeNull(); + }); +}); + +/** + * The overlay is drawn inside a box of the template's shape, not stretched + * over the picture. + * + * Stretching made the guides fit whatever they were laid over, so an image the + * caption called "off frame" had its eye line drawn exactly where a fitting + * image would -- the overlay quietly contradicting the badge beside it. + */ +describe("ImageLightbox guides on an image that does not fit", () => { + const square = (id: string) => ({ + id, + url: `https://example.com/${id}.jpg`, + width: 300, + height: 300, + }); + + const show = async (image: ReturnType) => { + const { user } = renderForm( + {}} + />, + ); + await user.click(screen.getByRole("button", { name: "Show guides" })); + return document.querySelector(".CropOverlay") as HTMLElement; + }; + + // A 2:3 frame in a square picture is full height and two thirds as wide. + it("shrinks the overlay to the template's shape", async () => { + const overlay = await show(square("a")); + + expect(overlay.style.height).toBe("100%"); + expect(Number.parseFloat(overlay.style.width)).toBeCloseTo(66.667, 2); + }); + + it("centres what is left over", async () => { + const overlay = await show(square("a")); + + expect(Number.parseFloat(overlay.style.left)).toBeCloseTo(16.667, 2); + expect(overlay.style.top).toBe("0%"); + }); + + // Without an edge the guides simply stop short of the photograph, which + // reads as them being misplaced rather than as the picture being too wide. + it("draws the frame edge", async () => { + const overlay = await show(square("a")); + expect(overlay.className).toContain("CropOverlay-inset"); + }); + + it("says so in the caption too", async () => { + await show(square("a")); + expect(screen.getByText(/off frame/)).toBeInTheDocument(); + }); + + // The common case must be untouched: a picture that fits gets the whole box + // and no edge drawn on top of its own. + it("leaves a fitting image alone", async () => { + const { user } = renderForm( + {}} + />, + ); + await user.click(screen.getByRole("button", { name: "Show guides" })); + + const overlay = document.querySelector(".CropOverlay") as HTMLElement; + expect(overlay.style.width).toBe("100%"); + expect(overlay.style.height).toBe("100%"); + expect(overlay.className).not.toContain("CropOverlay-inset"); + expect(screen.queryByText(/off frame/)).toBeNull(); + }); + + // The label margin is a fifth of the column. Reserving it for images that + // will never draw a label -- scene images, edit diffs, anything with no + // template -- shifts every lightbox in the app sideways for nothing. + // + // Reserving it only while the guides are drawn is the other way to get it + // wrong, and is what shipped first: the picture then jumps sideways every + // time the toggle is pressed. The room follows the template, not the toggle. + describe("the room reserved for guide labels", () => { + const guided = () => + document.querySelector(".ImageLightbox-main")?.className ?? ""; + + it("is not taken when the image has no template", () => { + setup(); + expect(guided()).not.toContain("ImageLightbox-main-guided"); + }); + + it("is taken as soon as there is a template, before any toggling", () => { + setup({ a: FACE }); + expect(guided()).toContain("ImageLightbox-main-guided"); + }); + + it("does not move the picture when the guides are shown", async () => { + const { user } = setup({ a: FACE }); + const before = guided(); + + await user.click(screen.getByRole("button", { name: "Show guides" })); + expect(guided()).toBe(before); + + await user.click(screen.getByRole("button", { name: "Hide guides" })); + expect(guided()).toBe(before); + }); + }); +}); + +// The toggle says whether it is on. The label changes too, which a sighted +// user reads -- but "Hide guides, button" announced on its own leaves it +// ambiguous whether the guides are showing or that is merely the offer. +describe("the guides toggle as a toggle", () => { + it("reports its state, not just its label", async () => { + const { user } = setup({ a: FACE }); + + const toggle = () => + screen.getByRole("button", { name: /(Show|Hide) guides/ }); + + expect(toggle()).toHaveAttribute("aria-pressed", "false"); + + await user.click(toggle()); + expect(toggle()).toHaveAttribute("aria-pressed", "true"); + + await user.click(toggle()); + expect(toggle()).toHaveAttribute("aria-pressed", "false"); + }); +}); + +/** + * The overlay must sit on the picture, not on the box the picture is in. + * + * The lightbox column constrains .Image on both axes, so the box is not always + * the image's shape; the picture then letterboxes inside it and an overlay + * drawn at 100% of the box reaches past the photograph. That would be worst in + * an edit diff, where the whole point is holding an image against its frame. + */ +describe("ImageLightbox guides on a box that is not the image's shape", () => { + const withBoxSize = (width: number, height: number) => { + const original = window.ResizeObserver; + window.ResizeObserver = class { + constructor(private cb: ResizeObserverCallback) {} + observe(el: Element) { + this.cb( + [ + { + target: el, + contentRect: { width, height }, + } as ResizeObserverEntry, + ], + this as unknown as ResizeObserver, + ); + } + unobserve() {} + disconnect() {} + } as unknown as typeof ResizeObserver; + return () => { + window.ResizeObserver = original; + }; + }; + + const overlayBox = () => + document.querySelector(".ImageLightbox-overlay-box > div"); + + it("insets the overlay to the picture when the box is wider", async () => { + const restore = withBoxSize(600, 300); // 2:1, against a 2:3 image + try { + const { user } = setup({ a: FACE }); + await user.click(screen.getByRole("button", { name: "Show guides" })); + + const box = overlayBox(); + expect(box).not.toBeNull(); + // A 2:3 picture in a 2:1 box fills the height and a third of the width. + expect(box?.style.height).toBe("100%"); + expect(Number.parseFloat(box?.style.width ?? "0")).toBeCloseTo(33.33, 1); + // Centred, so a third of the way in. + expect(Number.parseFloat(box?.style.left ?? "0")).toBeCloseTo(33.33, 1); + } finally { + restore(); + } + }); + + it("fills the box when it already matches the image", async () => { + const restore = withBoxSize(200, 300); // the image's own shape + try { + const { user } = setup({ a: FACE }); + await user.click(screen.getByRole("button", { name: "Show guides" })); + + const box = overlayBox(); + expect(box?.style.width).toBe("100%"); + expect(box?.style.height).toBe("100%"); + } finally { + restore(); + } + }); +}); + +/** + * The overlay is sized from a synchronous read in the ref callback, not from + * ResizeObserver's first delivery, which is asynchronous. Without that read the + * fallback geometry paints for a frame -- and because the lightbox keys its + * image on the url, the component remounts on every step through a gallery, so + * the wrong frame flashes each time rather than once at startup. + */ +describe("ImageLightbox guides arrive already fitted", () => { + // Measurable synchronously, like a browser; never delivers asynchronously, + // unlike one. Anything the overlay gets right here, it got from the sync read. + const withMeasurableBox = (width: number, height: number) => { + const observer = window.ResizeObserver; + const rect = Element.prototype.getBoundingClientRect; + + window.ResizeObserver = class { + observe() {} + unobserve() {} + disconnect() {} + } as unknown as typeof ResizeObserver; + + Element.prototype.getBoundingClientRect = function (this: Element) { + return this.classList.contains("ImageLightbox-overlay-box") + ? ({ width, height } as DOMRect) + : ({ width: 0, height: 0 } as DOMRect); + }; + + return () => { + window.ResizeObserver = observer; + Element.prototype.getBoundingClientRect = rect; + }; + }; + + it("fits on the first render, with no observer callback at all", async () => { + const restore = withMeasurableBox(600, 300); // 2:1, against a 2:3 image + try { + const { user } = setup({ a: FACE }); + await user.click(screen.getByRole("button", { name: "Show guides" })); + + const box = document.querySelector( + ".ImageLightbox-overlay-box > div", + ); + expect(Number.parseFloat(box?.style.width ?? "0")).toBeCloseTo(33.33, 1); + // Not hidden: hiding is what an environment that cannot measure gets. + expect(box?.style.visibility).toBe(""); + } finally { + restore(); + } + }); +}); diff --git a/frontend/src/components/image/index.ts b/frontend/src/components/image/index.ts index c6cd0de83..6a77f2774 100644 --- a/frontend/src/components/image/index.ts +++ b/frontend/src/components/image/index.ts @@ -1 +1,2 @@ +export type { LightboxProps } from "./Image"; export { default } from "./Image"; diff --git a/frontend/src/components/image/styles.scss b/frontend/src/components/image/styles.scss index 597e7e545..dc4f40a39 100644 --- a/frontend/src/components/image/styles.scss +++ b/frontend/src/components/image/styles.scss @@ -115,6 +115,12 @@ button.Image { justify-content: center; min-width: 0; + padding-top: 1rem; + + &-guided { + padding-left: min(8rem, 18%); + } + .Image { flex: 1; min-height: 0; @@ -123,6 +129,8 @@ button.Image { img { object-fit: contain; object-position: center; + height: 100%; + width: 100%; } } @@ -166,6 +174,59 @@ button.Image { width: 500px; } + // Labels in the corner of the image, hidden on hover + &-labels { + display: flex; + flex-wrap: wrap; + gap: 0.2rem; + justify-content: flex-end; + max-width: 100%; + transition: opacity 0.15s ease; + } + + &-label { + background-color: rgba(0 0 0 / 65%); + border-radius: 3px; + color: $text-color; + font-size: 0.75rem; + padding: 0.1rem 0.4rem; + white-space: nowrap; + } + + &-main .Image &-labels { + bottom: 0.5rem; + position: absolute; + right: 0.5rem; + z-index: 2; + } + + &-main .Image:hover &-labels { + opacity: 0; + } + + &-editor { + flex: 0 0 auto; + margin-top: 0.75rem; + width: 100%; + } + + &-thumb-labels { + bottom: 1.6rem; + left: 0.25rem; + position: absolute; + right: 0.25rem; + + .ImageLightbox-labels { + justify-content: flex-start; + opacity: 1; + } + + .ImageLightbox-label { + font-size: 0.6875rem; + padding: 0.05rem 0.3rem; + } + } + &-thumb { background: none; border: 2px solid rgba(255 255 255 / 70%); @@ -201,3 +262,21 @@ button.Image { } } } + +// Sits in the caption line next to the dimensions, so turning the guides off +// is where you are already looking when they are in the way +.ImageLightbox-guide-toggle { + font-size: inherit; + padding: 0; + vertical-align: baseline; +} + +.ImageLightbox-off-aspect { + opacity: 0.7; +} + +.ImageLightbox-overlay-box { + inset: 0; + pointer-events: none; + position: absolute; +} diff --git a/frontend/src/components/imageChangeRow/ImageChangeRow.tsx b/frontend/src/components/imageChangeRow/ImageChangeRow.tsx index 9d5990024..fce89475b 100644 --- a/frontend/src/components/imageChangeRow/ImageChangeRow.tsx +++ b/frontend/src/components/imageChangeRow/ImageChangeRow.tsx @@ -1,6 +1,8 @@ import type { FC } from "react"; -import { Col, Row } from "react-bootstrap"; +import { Badge, Col, Row } from "react-bootstrap"; +import type { CropTemplateInfo } from "src/components/cropFrame"; import ImageComponent from "src/components/image"; +import { useImageTypeVocabulary } from "src/hooks"; type Image = { height: number; @@ -11,73 +13,189 @@ type Image = { const CLASSNAME = "ImageChangeRow"; const CLASSNAME_IMAGE = `${CLASSNAME}-image`; +const CLASSNAME_GROUP = `${CLASSNAME}-group`; +const CLASSNAME_CHIPS = `${CLASSNAME}-chips`; + +export interface ImageAssignmentChange { + image: Image; + added_types: string[]; + removed_types: string[]; + date?: string | null; + date_changed: boolean; +} + +export interface ResultingImage { + image: Image; + types: string[]; + date?: string | null; +} export interface ImageChangeRowProps { newImages?: (Image | null)[] | null; oldImages?: (Image | null)[] | null; + changes?: ImageAssignmentChange[] | null; + // The lightbox needs to show the final result + resulting?: ResultingImage[] | null; showDiff?: boolean; } -const Images: FC<{ - images: (Image | null)[] | null | undefined; -}> = ({ images }) => { - const lightboxImages = (images ?? []).filter((image) => image !== null); +const ImageCell: FC<{ + image: Image; + change?: ImageAssignmentChange; + gallery: Image[]; + labels: Record; + cropTemplates: Record; + typeName: (key: string) => string; + typeDescription: (key: string) => string | undefined; +}> = ({ + image, + change, + gallery, + labels, + cropTemplates, + typeName, + typeDescription, +}) => ( +
+ +
+ {image.width} x {image.height} +
- return ( - <> - {(images ?? []).map((image, i) => - image === null ? ( - // biome-ignore lint/suspicious/noArrayIndexKey: Image is deleted, no other key - Deleted - ) : ( -
- -
- {image.width} x {image.height} -
-
- ), - )} - - ); -}; + {change && ( +
+ {change.added_types.map((type) => ( + + + {typeName(type)} + + ))} + {change.removed_types.map((type) => ( + + − {typeName(type)} + + ))} + {change.date_changed && + (change.date ? ( + Date {change.date} + ) : ( + Date cleared + ))} +
+ )} +
+); +/** + * Everything this edit does to an entity's gallery, in one row + * + * One cell per image, and every image appears exactly once. Grouped by outcome + * rather than split into Removed | Added columns, because there are three: an + * image can be added, removed, or kept and relabelled + */ const ImageChangeRow: FC = ({ newImages, oldImages, + changes, + resulting, showDiff = false, -}) => - (newImages ?? []).length > 0 || (oldImages ?? []).length > 0 ? ( +}) => { + const { typeName, typeDescription, templateFor } = useImageTypeVocabulary(); + + const added = (newImages ?? []).filter((image) => image !== null); + const removed = (oldImages ?? []).filter((image) => image !== null); + const deletedCount = (oldImages ?? []).length - removed.length; + + const changeFor = new Map((changes ?? []).map((c) => [c.image.id, c])); + const addedIDs = new Set(added.map((image) => image.id)); + const removedIDs = new Set(removed.map((image) => image.id)); + + // Kept, but relabelled or redated + const relabelled = (changes ?? []) + .filter((c) => !addedIDs.has(c.image.id) && !removedIDs.has(c.image.id)) + .map((c) => c.image); + + const gallery = [...added, ...relabelled, ...removed]; + + const labels: Record = {}; + const cropTemplates: Record = {}; + for (const entry of resulting ?? []) { + labels[entry.image.id] = entry.types.map(typeName); + const template = templateFor(entry.types); + if (template) cropTemplates[entry.image.id] = template; + } + + const cell = (image: Image) => ( + + ); + + if ( + added.length === 0 && + removed.length === 0 && + relabelled.length === 0 && + deletedCount === 0 + ) + return null; + + return ( Images - {showDiff && ( - - {(oldImages ?? []).length > 0 && ( - <> -
Removed
-
- -
- - )} - - )} - - {(newImages ?? []).length > 0 && ( - <> - {showDiff &&
Added
} + + {showDiff && (removed.length > 0 || deletedCount > 0) && ( +
+
Removed
- + {removed.map(cell)} + {Array.from({ length: deletedCount }, (_, i) => ( + Deleted + ))}
- +
+ )} + + {added.length > 0 && ( +
+ {showDiff &&
Added
} +
{added.map(cell)}
+
+ )} + + {relabelled.length > 0 && ( +
+
Relabelled
+
{relabelled.map(cell)}
+
)}
- ) : null; + ); +}; export default ImageChangeRow; diff --git a/frontend/src/components/imageChangeRow/index.ts b/frontend/src/components/imageChangeRow/index.ts index e6fb1abf2..52476f2f4 100644 --- a/frontend/src/components/imageChangeRow/index.ts +++ b/frontend/src/components/imageChangeRow/index.ts @@ -1 +1,5 @@ +export type { + ImageAssignmentChange, + ResultingImage, +} from "./ImageChangeRow"; export { default } from "./ImageChangeRow"; diff --git a/frontend/src/components/imageChangeRow/styles.scss b/frontend/src/components/imageChangeRow/styles.scss index 6357315a7..56079354e 100644 --- a/frontend/src/components/imageChangeRow/styles.scss +++ b/frontend/src/components/imageChangeRow/styles.scss @@ -3,8 +3,41 @@ flex-wrap: wrap; .Image { + align-items: center; + align-self: center; height: 150px; + justify-content: center; margin: 5px; + max-width: 100%; width: auto; } + + .Image img { + object-fit: contain; + } + + // Without the gap the three bands read as one long strip of thumbnails and + // the headings stop dividing anything + &-group + &-group { + margin-top: 0.75rem; + } + + // Sized to its own picture so the chips wrap under it rather than stretching + // the cell and pushing the next image along + // fit-content to account for different aspect ratios of portraits and wide shotw + &-image { + display: flex; + flex-direction: column; + max-width: 20rem; + min-width: 9rem; + width: fit-content; + } + + &-chips { + display: flex; + flex-wrap: wrap; + gap: 0.2rem; + justify-content: center; + margin: 0.25rem 5px 0; + } } diff --git a/frontend/src/components/searchField/SearchField.tsx b/frontend/src/components/searchField/SearchField.tsx index e33d5d263..7ad8b58cc 100644 --- a/frontend/src/components/searchField/SearchField.tsx +++ b/frontend/src/components/searchField/SearchField.tsx @@ -64,7 +64,7 @@ const formatOptionLabel = ({ label, sublabel, value }: SearchResult) => (
{valueIsPerformer(value) && ( , +) => useMutation(UpdateImageTypeOrderDocument, options); + +export const useSetImageTypeEnabled = ( + options?: useMutation.Options< + SetImageTypeEnabledMutation, + SetImageTypeEnabledMutationVariables + >, +) => useMutation(SetImageTypeEnabledDocument, options); + +export const useUpdateImageTypePreferences = ( + options?: useMutation.Options< + UpdateImageTypePreferencesMutation, + UpdateImageTypePreferencesMutationVariables + >, +) => useMutation(UpdateImageTypePreferencesDocument, options); diff --git a/frontend/src/graphql/queries/ImageTypeGroups.gql b/frontend/src/graphql/queries/ImageTypeGroups.gql new file mode 100644 index 000000000..4377c88be --- /dev/null +++ b/frontend/src/graphql/queries/ImageTypeGroups.gql @@ -0,0 +1,45 @@ +query ImageTypeGroups($target: ImageTypeScopeEnum, $includeDisabled: Boolean) { + imageTypeGroups(target: $target, include_disabled: $includeDisabled) { + key + name + description + enabled + types { + key + name + description + enabled + conflicts_with + crop_template { + aspect_ratio + guides { + axis + position + role + label + pivot + } + shapes { + label + subpaths { + closed + knots { + control_in { + x + y + } + anchor { + x + y + } + control_out { + x + y + } + } + } + } + } + } + } +} diff --git a/frontend/src/graphql/queries/User.gql b/frontend/src/graphql/queries/User.gql index c63d61a93..ffaa7abff 100644 --- a/frontend/src/graphql/queries/User.gql +++ b/frontend/src/graphql/queries/User.gql @@ -40,5 +40,7 @@ query User($name: String!) { pending_bot } notification_subscriptions + image_type_preferences + image_type_group_preferences } } diff --git a/frontend/src/graphql/queries/index.ts b/frontend/src/graphql/queries/index.ts index 60eb51eb2..a22ca40a6 100644 --- a/frontend/src/graphql/queries/index.ts +++ b/frontend/src/graphql/queries/index.ts @@ -18,6 +18,8 @@ import { FingerprintClustersDocument, type FingerprintClustersQueryVariables, FullPerformerDocument, + ImageTypeGroupsDocument, + type ImageTypeGroupsQueryVariables, MeDocument, type MeQuery, type MeQueryVariables, @@ -98,6 +100,9 @@ export const useCategory = (variables: CategoryQueryVariables, skip = false) => export const useCategories = () => useQuery(CategoriesDocument); +export const useImageTypeGroups = (variables: ImageTypeGroupsQueryVariables) => + useQuery(ImageTypeGroupsDocument, { variables }); + export const useEdit = (variables: EditQueryVariables, skip = false) => useQuery(EditDocument, { variables, diff --git a/frontend/src/graphql/types.ts b/frontend/src/graphql/types.ts index 2078808db..c1db83194 100644 --- a/frontend/src/graphql/types.ts +++ b/frontend/src/graphql/types.ts @@ -136,6 +136,131 @@ export enum CriterionModifier { NOT_NULL = 'NOT_NULL' } +/** One guide line of a crop template */ +export type CropGuide = { + __typename: 'CropGuide'; + axis: CropGuideAxisEnum; + /** + * What the line is for, like "bisects the eyes", "where the thighs meet", or + * null when the template does not name it + */ + label?: Maybe; + /** + * Whether a frame is resized around this line when the contributor holds + * Shift + * + * Independent of `role`, which says how closely a line is meant to be + * followed. A headshot's eye line is the softest line in its template (like the + * head and chin can be hard limits) and is still the right thing to turn a + * resize about, so the two cannot be the same field + * + * At most one guide per axis carries it. A template naming none on an axis + * resizes about the centre there + */ + pivot: Scalars['Boolean']['output']; + /** + * Where the line sits, as a fraction of the canvas along its axis: 0 is the + * left or top edge, 1 the right or bottom. A fraction rather than a pixel + * because a template is drawn at one size and rendered at every other + */ + position: Scalars['Float']['output']; + /** + * How closely the line is meant to be followed, where the template says. An + * anchor is meant to be hit; a reference is for judgement and balance + */ + role?: Maybe; +}; + +export enum CropGuideAxisEnum { + /** A vertical line, positioned across the width */ + X = 'X', + /** A horizontal line, positioned down the height */ + Y = 'Y' +} + +export enum CropGuideRoleEnum { + ANCHOR = 'ANCHOR', + MARGIN = 'MARGIN', + REFERENCE = 'REFERENCE' +} + +/** + * One anchor of an outline, with the control point either side of it + * + * Every segment is a cubic curve, including straight ones. Photoshop draws a + * straight edge as a curve whose controls sit on its anchors, so a rectangle and + * an ellipse arrive in the same shape + */ +export type CropKnot = { + __typename: 'CropKnot'; + anchor: CropPoint; + /** The control point governing the curve arriving at this anchor */ + control_in: CropPoint; + /** The control point governing the curve leaving it */ + control_out: CropPoint; +}; + +/** + * A position on the template's canvas, as fractions of its width and height + * + * Fractions like a guide's position, and for the same reason: a template is drawn + * at one size and rendered at every other. Values outside 0 to 1 are legitimate: + * a crop box is often drawn a hair outside the canvas so its stroke does not eat + * into the picture + */ +export type CropPoint = { + __typename: 'CropPoint'; + x: Scalars['Float']['output']; + y: Scalars['Float']['output']; +}; + +/** One outline drawn in a crop template */ +export type CropShape = { + __typename: 'CropShape'; + /** + * What the template's author called the layer, like "head guide", "eyes soft + * anchor", or null for an unnamed layer + */ + label?: Maybe; + subpaths: Array; +}; + +/** + * One continuous run of a shape's outline + * + * A shape can be several: a ring is an outer subpath and an inner one, and + * whether each closes back on itself is the difference between an outline and an + * arc + */ +export type CropSubpath = { + __typename: 'CropSubpath'; + closed: Scalars['Boolean']['output']; + knots: Array; +}; + +/** + * A crop frame, read from a Photoshop template + * + * The template file is the source of truth: the guides drawn over the cropping + * tool and the .psd a contributor can download for their own editor are the same + * bytes, so the two cannot drift + */ +export type CropTemplate = { + __typename: 'CropTemplate'; + /** + * Width over height, taken from the template's canvas rather than set + * anywhere + */ + aspect_ratio: Scalars['Float']['output']; + guides: Array; + /** + * Outlines drawn on the template's own layers like an oval for a face to sit + * inside, a bar marking a margin. Guidance only: the crop is still a + * rectangle, and nothing here changes what the server cuts + */ + shapes: Array; +}; + export enum DateAccuracyEnum { DAY = 'DAY', MONTH = 'MONTH', @@ -579,15 +704,258 @@ export type Image = { width: Scalars['Int']['output']; }; +/** + * What an edit changes about one image's labels and date, grouped by image + * rather than listed as flat added/removed tuples: one performer edit can + * relabel a whole gallery. + */ +export type ImageAssignmentChange = { + __typename: 'ImageAssignmentChange'; + added_types: Array; + /** + * The date this edit sets. Only meaningful when date_changed is true, where a + * null means the edit clears the date. + */ + date?: Maybe; + /** Whether this edit changes the image's date at all. */ + date_changed: Scalars['Boolean']['output']; + image: Image; + removed_types: Array; +}; + +/** + * Everything said about one image's presence on an entity. An entry whose types + * are empty clears that image's labels. + * + * **What each way of sending `image_types` means.** Three write paths implement + * this: `performerCreate` and `performerUpdate` in Go, and the edit path in Go + * at submission and SQL at apply. Nothing makes them agree but this table. + * + * | `image_types` is | performerCreate | performerUpdate | edit | + * |---|---|---|---| + * | absent | unlabelled | preserves all | preserves all | + * | explicit `null` | unlabelled | **preserves all** | **clears all** | + * | `[]` | unlabelled | clears all | clears all | + * | non-empty | labels the images named | authoritative only over the images named | authoritative only over the images named | + * + * Null differs because the edit path is told which fields the client stated and + * `performerUpdate` is not. **Send `[]` to clear on any path** and the question + * does not arise. + * + * Note that a non-empty list leaves an image it does not mention exactly as it + * was; otherwise every client touching `image_ids` would have to restate the + * whole gallery's labels or destroy them. + * + * **And the same for `date`,** which is single-valued and so overrides + * rather than merges: + * + * | the submission | the image's date | + * |---|---| + * | no entry for this image | kept | + * | an entry stating `date` | set | + * | an entry omitting `date` | cleared, see the field's own note | + * | an entry omitting it, on an image being added | stays empty, and is not reported as a change | + */ +export type ImageAssignmentInput = { + /** + * When the image is from. Partial ISO 8601: 2019, 2019-06, or 2019-06-15. + * + * An entry states the whole of what is true about its image, so omitting this + * clears the date rather than leaving it. Send the current value back if the + * change is only to the labels. + */ + date?: InputMaybe; + image_id: Scalars['ID']['input']; + types: Array; +}; + export type ImageCreateInput = { + crop?: InputMaybe; file?: InputMaybe; url?: InputMaybe; }; +/** + * A frame to cut an upload down to, in the coordinates the client is looking at. + * + * Cropping happens here rather than in the browser for two reasons. A canvas + * re-encode is a second lossy generation on top of whatever the contributor + * started with, where the server decodes once and encodes once. And images are + * deduplicated on a checksum of their stored bytes, which stops working if the + * bytes are produced by whichever encoder the uploader's browser happens to + * have: two people cropping the same source to the same frame would land as two + * images + */ +export type ImageCropInput = { + /** + * Degrees to rotate clockwise before cutting, for a tilted horizon. The frame + * above is measured against the rotated image, which is larger than the + * original - the same thing the client is dragging over. + * + * EXIF orientation is applied before any of this, so the coordinates are the + * ones a browser shows rather than the ones stored in the file + */ + angle?: InputMaybe; + /** Fraction of the height to keep */ + height: Scalars['Float']['input']; + /** Fraction of the width to keep */ + width: Scalars['Float']['input']; + /** Distance from the left edge, as a fraction of the width */ + x: Scalars['Float']['input']; + /** Distance from the top edge, as a fraction of the height */ + y: Scalars['Float']['input']; +}; + export type ImageDestroyInput = { id: Scalars['ID']['input']; }; +export type ImageType = { + __typename: 'ImageType'; + /** + * Types this one cannot share an image with, across groups: a face crop cannot + * be topless, because the chest is not in frame. Symmetric: each side + * of a pair lists the other. Assigning both is rejected; a client should stop + * offering the second once the first is chosen. + */ + conflicts_with: Array; + /** + * The frame to crop to for this type, or null if the instance has no template + * for it. Only crops have one - nothing about a pose or a state of dress says + * anything about the shape of the picture + */ + crop_template?: Maybe; + description?: Maybe; + /** Whether this instance uses this type. Disabled types cannot be assigned. */ + enabled: Scalars['Boolean']['output']; + key: ImageTypeEnum; + name: Scalars['String']['output']; + /** Value priority within the group; lower wins */ + sort_order: Scalars['Int']['output']; + valid_types: Array; +}; + +/** + * Which parts of the vocabulary an instance switches off. + * + * Expressed as what is disabled rather than what is enabled, so a type added to + * the taxonomy later arrives switched on. + */ +export type ImageTypeEnabledInput = { + /** Groups to switch off. A group being off implies its types are too. */ + disabled_groups?: Array; + /** Types to switch off individually, whatever their group's state. */ + disabled_types?: Array; +}; + +/** + * A label that may be applied to an image's presence on an entity. + * + * Every key is its group key followed by an underscore, so SHOT_PORTRAIT belongs + * to the SHOT group. The vocabulary is fixed and identical on every instance, + * which is what lets a client code against these values directly. + */ +export enum ImageTypeEnum { + CROP_BUST = 'CROP_BUST', + CROP_FACE = 'CROP_FACE', + CROP_FULL_BODY = 'CROP_FULL_BODY', + CROP_THREE_QUARTER = 'CROP_THREE_QUARTER', + CROP_THREE_QUARTER_PLUS = 'CROP_THREE_QUARTER_PLUS', + CROP_TORSO = 'CROP_TORSO', + CROP_WIDE = 'CROP_WIDE', + DRESS_EXPLICIT = 'DRESS_EXPLICIT', + DRESS_NON_NUDE = 'DRESS_NON_NUDE', + DRESS_NUDE = 'DRESS_NUDE', + DRESS_TOPLESS = 'DRESS_TOPLESS', + DRESS_UNDERWEAR = 'DRESS_UNDERWEAR', + POSTURE_KNEELING = 'POSTURE_KNEELING', + POSTURE_LYING = 'POSTURE_LYING', + POSTURE_ON_ALL_FOURS = 'POSTURE_ON_ALL_FOURS', + POSTURE_SITTING = 'POSTURE_SITTING', + POSTURE_SQUATTING = 'POSTURE_SQUATTING', + POSTURE_STANDING = 'POSTURE_STANDING', + POSTURE_SUSPENDED = 'POSTURE_SUSPENDED', + SHOT_CANDID = 'SHOT_CANDID', + SHOT_DETAIL = 'SHOT_DETAIL', + SHOT_PORTRAIT = 'SHOT_PORTRAIT', + VIEW_BACK = 'VIEW_BACK', + VIEW_FRONT = 'VIEW_FRONT', + VIEW_SIDE = 'VIEW_SIDE' +} + +export type ImageTypeGroup = { + __typename: 'ImageTypeGroup'; + description?: Maybe; + /** + * Whether this instance uses this dimension. A disabled group is not offered + * when labelling and takes no part in ranking; existing assignments are kept, + * so re-enabling restores them. + */ + enabled: Scalars['Boolean']['output']; + /** At most one type from this group may be assigned to an image */ + exclusive: Scalars['Boolean']['output']; + key: ImageTypeGroupEnum; + name: Scalars['String']['output']; + /** Dimension priority when ranking images; lower wins */ + sort_order: Scalars['Int']['output']; + types: Array; +}; + +/** A dimension of the image type vocabulary. Types within one group are ranked against each other. */ +export enum ImageTypeGroupEnum { + CROP = 'CROP', + DRESS = 'DRESS', + POSTURE = 'POSTURE', + SHOT = 'SHOT', + VIEW = 'VIEW' +} + +/** + * A complete reordering of the vocabulary. Partial lists are rejected rather than + * merged. + */ +export type ImageTypeOrderInput = { + /** Groups in priority order. Must list every group exactly once. */ + groups: Array; + /** + * Types in priority order. Must list every type exactly once. Only position + * within each group counts, so types of different groups may interleave freely. + */ + types: Array; +}; + +/** + * One user's ranking. Unlike the admin ordering both lists may be partial: a user + * says what they care about and everything else keeps the instance order behind + * it, which is what lets someone express "nudes first" without having to rank all + * seventeen types. + */ +export type ImageTypePreferencesInput = { + /** + * Groups in preferred order, deciding which dimension is compared first. + * + * Absent leaves the group preference as it is; an empty list clears it. Not + * defaulted, so a client sending only `types` keeps the group ordering it did + * not mention. + */ + groups?: InputMaybe>; + /** Types in preferred order, position within each group being what counts. */ + types: Array; +}; + +/** + * The kinds of entity an image type may be applied to. + * + * Every value seeded today is PERFORMER-only. When scenes and studios get + * image labelling, they get their own separate types and groups, not rows + * here with SCENE or STUDIO added to a type's `valid_types` + */ +export enum ImageTypeScopeEnum { + PERFORMER = 'PERFORMER', + SCENE = 'SCENE', + STUDIO = 'STUDIO' +} + export type ImageUpdateInput = { id: Scalars['ID']['input']; url?: InputMaybe; @@ -693,6 +1061,18 @@ export type Mutation = { hideEditComment: EditComment; imageCreate?: Maybe; imageDestroy: Scalars['Boolean']['output']; + /** + * Reorder the image type vocabulary, deciding which image ranks first + * instance-wide. Both lists must be complete; returns the reordered vocabulary. + */ + imageTypeOrderUpdate: Array; + /** + * Choose which of the vocabulary this instance uses. Takes the complete set of + * keys to switch off, so anything absent is on; returns the whole vocabulary, + * disabled entries included. Nothing is deleted, so switching a group back on + * restores every label made while it was in use. + */ + imageTypeSetEnabled: Array; /** Mark all of the current users notifications as read. */ markNotificationsRead: Scalars['Boolean']['output']; /** User interface for registering */ @@ -757,6 +1137,12 @@ export type Mutation = { tagUpdate?: Maybe; /** Edit a comment's text - moderator only */ updateEditComment: EditComment; + /** + * Reorder image types for the current user, and optionally the groups they sit + * in. Unlike the admin ordering both lists may be partial: anything left out + * trails what was listed, in instance order. Empty lists clear that preference. + */ + updateImageTypePreferences: Scalars['Boolean']['output']; /** Update notification subscriptions for current user. */ updateNotificationSubscriptions: Scalars['Boolean']['output']; userCreate?: Maybe; @@ -853,6 +1239,16 @@ export type MutationImageDestroyArgs = { }; +export type MutationImageTypeOrderUpdateArgs = { + input: ImageTypeOrderInput; +}; + + +export type MutationImageTypeSetEnabledArgs = { + input: ImageTypeEnabledInput; +}; + + export type MutationMarkNotificationsReadArgs = { notification?: InputMaybe; }; @@ -1067,6 +1463,11 @@ export type MutationUpdateEditCommentArgs = { }; +export type MutationUpdateImageTypePreferencesArgs = { + input: ImageTypePreferencesInput; +}; + + export type MutationUpdateNotificationSubscriptionsArgs = { subscriptions: Array; }; @@ -1160,6 +1561,11 @@ export type Performer = { height?: Maybe; hip_size?: Maybe; id: Scalars['ID']['output']; + /** + * The gallery, ordered as this viewer ranks image types. Anywhere one image + * stands for the performer, that is `images[0]`: a card, a grid, a merge + * target. + */ images: Array; is_favorite: Scalars['Boolean']['output']; /** @deprecated Use individual fields, cup/band/waist/hip_size */ @@ -1176,6 +1582,17 @@ export type Performer = { scenes: Array; studios: Array; tattoos?: Maybe>; + /** + * The most recognisable image, for search results and dropdowns only. + * + * Always prefers a face crop and ignores the viewer's type preference: + * legibility at thumbnail size is not a matter of taste, and being the same + * for everyone is what lets it be cached. Everywhere else wants `images[0]`, + * which does follow the viewer. + */ + thumbnail?: Maybe; + /** The same images, each with the types it has been labelled with here */ + typed_images: Array; updated: Scalars['Time']['output']; urls: Array; waist_size?: Maybe; @@ -1228,6 +1645,11 @@ export type PerformerCreateInput = { height?: InputMaybe; hip_size?: InputMaybe; image_ids?: InputMaybe>; + /** + * Labels for the images named. An image in image_ids with no entry here is + * simply unlabelled; there is nothing to preserve on a create. + */ + image_types?: InputMaybe>; name: Scalars['String']['input']; piercings?: InputMaybe>; tattoos?: InputMaybe>; @@ -1311,6 +1733,8 @@ export type PerformerEdit = { /** Height in cm */ height?: Maybe; hip_size?: Maybe; + /** Label and date changes, one entry per affected image */ + image_changes: Array; images: Array; name?: Maybe; piercings: Array; @@ -1320,6 +1744,15 @@ export type PerformerEdit = { removed_tattoos?: Maybe>; removed_urls?: Maybe>; tattoos: Array; + /** + * The gallery this edit results in: each surviving image with the labels and + * date it will carry once applied. + * + * The state being voted on, as opposed to image_changes, which is what moves. + * A reviewer opening an image wants to see what it will be, the same way the + * gallery lightbox shows it. + */ + typed_images: Array; urls: Array; waist_size?: Maybe; }; @@ -1343,6 +1776,16 @@ export type PerformerEditDetailsInput = { height?: InputMaybe; hip_size?: InputMaybe; image_ids?: InputMaybe>; + /** + * Labels for the images named. Omitting the field leaves assignments alone; + * null or an empty list clears them all, matching image_ids. A non-empty list + * is authoritative only over the images it names. + * + * Null clearing here and preserving on performerUpdate is not a rule, it is + * what each path can see: this one is told which fields the client stated, and + * that one is not. Send an empty list to clear, on either path. + */ + image_types?: InputMaybe>; name?: InputMaybe; piercings?: InputMaybe>; tattoos?: InputMaybe>; @@ -1472,6 +1915,17 @@ export type PerformerUpdateInput = { hip_size?: InputMaybe; id: Scalars['ID']['input']; image_ids?: InputMaybe>; + /** + * Labels for the images named. Absent leaves every assignment untouched, an + * empty list clears them all, and an image in image_ids with no entry here + * keeps what it has. + * + * Explicit null behaves as absent and preserves, which differs from the edit + * path, where it clears. This path is not told which fields the client stated, + * so it cannot tell an omitted field from one set to null; the edit path is, + * and does. Send an empty list to clear, on either path. + */ + image_types?: InputMaybe>; name?: InputMaybe; piercings?: InputMaybe>; tattoos?: InputMaybe>; @@ -1538,6 +1992,16 @@ export type Query = { fingerprintClusters: FingerprintClustersResult; getConfig: StashBoxConfig; getUnreadNotificationCount: UnreadNotificationCount; + /** + * The image type vocabulary, groups in priority order with their types nested. + * Filtering by target drops types that entity kind cannot carry, and drops any + * group thereby left empty. + * + * Disabled groups and types are omitted unless asked for: a labeller should not + * see what the instance has switched off, but the admin who switched it off has + * to be able to switch it back on. + */ + imageTypeGroups: Array; /** Returns currently authenticated user */ me?: Maybe; queryEdits: QueryEditsResultType; @@ -1676,6 +2140,13 @@ export type QueryFingerprintClustersArgs = { }; +/** The query root for this schema */ +export type QueryImageTypeGroupsArgs = { + include_disabled?: InputMaybe; + target?: InputMaybe; +}; + + /** The query root for this schema */ export type QueryQueryEditsArgs = { input: EditQueryInput; @@ -2435,6 +2906,18 @@ export enum TargetTypeEnum { TAG = 'TAG' } +/** + * An image together with what it has been labelled on this entity. Not + * performer-specific: scenes and studios expose the same type. + */ +export type TypedImage = { + __typename: 'TypedImage'; + /** When the image is from. Partial ISO 8601: 2019, 2019-06, or 2019-06-15. */ + date?: Maybe; + image: Image; + types: Array; +}; + export type Url = { __typename: 'URL'; site: Site; @@ -2480,6 +2963,10 @@ export type User = { /** Should not be visible to other users */ email?: Maybe; id: Scalars['ID']['output']; + /** Preferred order of the groups themselves, deciding which dimension is compared first. Empty means the instance order. */ + image_type_group_preferences: Array; + /** Preferred order of types within their group, when ranking images. Empty means no preference. */ + image_type_preferences: Array; invite_codes?: Maybe>; invite_tokens?: Maybe; invited_by?: Maybe; @@ -2631,22 +3118,22 @@ export enum VoteTypeEnum { export type CommentFragment = { __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }; export type EditFragment = { __typename: 'Edit', id: string, target_type: TargetTypeEnum, operation: OperationEnum, status: VoteStatusEnum, bot: boolean, applied: boolean, created: string, updated?: string | null, closed?: string | null, expires?: string | null, passing?: boolean | null, update_count: number, updatable: boolean, vote_count: number, destructive: boolean, comments: Array<{ __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }>, votes: Array<{ __typename: 'EditVote', date: string, vote: VoteTypeEnum, user?: { __typename: 'User', id: string, name: string } | null }>, user?: { __typename: 'User', id: string, name: string } | null, target?: - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, details?: - | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, image_changes: Array<{ __typename: 'ImageAssignmentChange', added_types: Array, removed_types: Array, date?: string | null, date_changed: boolean, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, old_details?: | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, merge_sources: Array< - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } @@ -2677,7 +3164,7 @@ export type NotificationSceneFragment = { __typename: 'Scene', id: string, title export type PairingSceneFragment = { __typename: 'Scene', id: string, title?: string | null, duration?: number | null, release_date?: string | null, studio?: { __typename: 'Studio', id: string, name: string } | null, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> }; -export type PerformerFragment = { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> }; +export type PerformerFragment = { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> }; export type QuerySceneFragment = { __typename: 'Scene', id: string, release_date?: string | null, title?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }> }; @@ -2685,12 +3172,14 @@ export type SceneFragment = { __typename: 'Scene', id: string, release_date?: st export type ScenePerformerFragment = { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array }; -export type SearchPerformerFragment = { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array, country?: string | null, career_start_year?: number | null, career_end_year?: number | null, scene_count: number, birth_date?: string | null, is_favorite: boolean, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> }; +export type SearchPerformerFragment = { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array, country?: string | null, career_start_year?: number | null, career_end_year?: number | null, scene_count: number, birth_date?: string | null, is_favorite: boolean, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, thumbnail?: { __typename: 'Image', url: string } | null }; export type StudioFragment = { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> }; export type TagFragment = { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }; +export type TypedImageFragment = { __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }; + export type UrlFragment = { __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }; export type ActivateNewUserMutationVariables = Exact<{ @@ -2755,22 +3244,22 @@ export type AmendEditMutationVariables = Exact<{ export type AmendEditMutation = { __typename: 'Mutation', amendEdit: { __typename: 'Edit', id: string, target_type: TargetTypeEnum, operation: OperationEnum, status: VoteStatusEnum, bot: boolean, applied: boolean, created: string, updated?: string | null, closed?: string | null, expires?: string | null, passing?: boolean | null, update_count: number, updatable: boolean, vote_count: number, destructive: boolean, comments: Array<{ __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }>, votes: Array<{ __typename: 'EditVote', date: string, vote: VoteTypeEnum, user?: { __typename: 'User', id: string, name: string } | null }>, user?: { __typename: 'User', id: string, name: string } | null, target?: - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, details?: - | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, image_changes: Array<{ __typename: 'ImageAssignmentChange', added_types: Array, removed_types: Array, date?: string | null, date_changed: boolean, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, old_details?: | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, merge_sources: Array< - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } @@ -2782,22 +3271,22 @@ export type ApproveEditMutationVariables = Exact<{ export type ApproveEditMutation = { __typename: 'Mutation', approveEdit: { __typename: 'Edit', id: string, target_type: TargetTypeEnum, operation: OperationEnum, status: VoteStatusEnum, bot: boolean, applied: boolean, created: string, updated?: string | null, closed?: string | null, expires?: string | null, passing?: boolean | null, update_count: number, updatable: boolean, vote_count: number, destructive: boolean, comments: Array<{ __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }>, votes: Array<{ __typename: 'EditVote', date: string, vote: VoteTypeEnum, user?: { __typename: 'User', id: string, name: string } | null }>, user?: { __typename: 'User', id: string, name: string } | null, target?: - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, details?: - | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, image_changes: Array<{ __typename: 'ImageAssignmentChange', added_types: Array, removed_types: Array, date?: string | null, date_changed: boolean, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, old_details?: | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, merge_sources: Array< - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } @@ -2978,22 +3467,22 @@ export type PerformerEditMutationVariables = Exact<{ export type PerformerEditMutation = { __typename: 'Mutation', performerEdit: { __typename: 'Edit', id: string, target_type: TargetTypeEnum, operation: OperationEnum, status: VoteStatusEnum, bot: boolean, applied: boolean, created: string, updated?: string | null, closed?: string | null, expires?: string | null, passing?: boolean | null, update_count: number, updatable: boolean, vote_count: number, destructive: boolean, comments: Array<{ __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }>, votes: Array<{ __typename: 'EditVote', date: string, vote: VoteTypeEnum, user?: { __typename: 'User', id: string, name: string } | null }>, user?: { __typename: 'User', id: string, name: string } | null, target?: - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, details?: - | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, image_changes: Array<{ __typename: 'ImageAssignmentChange', added_types: Array, removed_types: Array, date?: string | null, date_changed: boolean, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, old_details?: | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, merge_sources: Array< - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } @@ -3006,22 +3495,22 @@ export type PerformerEditUpdateMutationVariables = Exact<{ export type PerformerEditUpdateMutation = { __typename: 'Mutation', performerEditUpdate: { __typename: 'Edit', id: string, target_type: TargetTypeEnum, operation: OperationEnum, status: VoteStatusEnum, bot: boolean, applied: boolean, created: string, updated?: string | null, closed?: string | null, expires?: string | null, passing?: boolean | null, update_count: number, updatable: boolean, vote_count: number, destructive: boolean, comments: Array<{ __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }>, votes: Array<{ __typename: 'EditVote', date: string, vote: VoteTypeEnum, user?: { __typename: 'User', id: string, name: string } | null }>, user?: { __typename: 'User', id: string, name: string } | null, target?: - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, details?: - | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, image_changes: Array<{ __typename: 'ImageAssignmentChange', added_types: Array, removed_types: Array, date?: string | null, date_changed: boolean, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, old_details?: | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, merge_sources: Array< - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } @@ -3066,22 +3555,22 @@ export type SceneEditMutationVariables = Exact<{ export type SceneEditMutation = { __typename: 'Mutation', sceneEdit: { __typename: 'Edit', id: string, target_type: TargetTypeEnum, operation: OperationEnum, status: VoteStatusEnum, bot: boolean, applied: boolean, created: string, updated?: string | null, closed?: string | null, expires?: string | null, passing?: boolean | null, update_count: number, updatable: boolean, vote_count: number, destructive: boolean, comments: Array<{ __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }>, votes: Array<{ __typename: 'EditVote', date: string, vote: VoteTypeEnum, user?: { __typename: 'User', id: string, name: string } | null }>, user?: { __typename: 'User', id: string, name: string } | null, target?: - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, details?: - | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, image_changes: Array<{ __typename: 'ImageAssignmentChange', added_types: Array, removed_types: Array, date?: string | null, date_changed: boolean, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, old_details?: | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, merge_sources: Array< - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } @@ -3094,49 +3583,56 @@ export type SceneEditUpdateMutationVariables = Exact<{ export type SceneEditUpdateMutation = { __typename: 'Mutation', sceneEditUpdate: { __typename: 'Edit', id: string, target_type: TargetTypeEnum, operation: OperationEnum, status: VoteStatusEnum, bot: boolean, applied: boolean, created: string, updated?: string | null, closed?: string | null, expires?: string | null, passing?: boolean | null, update_count: number, updatable: boolean, vote_count: number, destructive: boolean, comments: Array<{ __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }>, votes: Array<{ __typename: 'EditVote', date: string, vote: VoteTypeEnum, user?: { __typename: 'User', id: string, name: string } | null }>, user?: { __typename: 'User', id: string, name: string } | null, target?: - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, details?: - | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, image_changes: Array<{ __typename: 'ImageAssignmentChange', added_types: Array, removed_types: Array, date?: string | null, date_changed: boolean, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, old_details?: | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, merge_sources: Array< - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } >, options?: { __typename: 'PerformerEditOptions', set_modify_aliases: boolean, set_merge_aliases: boolean } | null } }; +export type SetImageTypeEnabledMutationVariables = Exact<{ + input: ImageTypeEnabledInput; +}>; + + +export type SetImageTypeEnabledMutation = { __typename: 'Mutation', imageTypeSetEnabled: Array<{ __typename: 'ImageTypeGroup', key: ImageTypeGroupEnum, enabled: boolean, types: Array<{ __typename: 'ImageType', key: ImageTypeEnum, enabled: boolean }> }> }; + export type StudioEditMutationVariables = Exact<{ studioData: StudioEditInput; }>; export type StudioEditMutation = { __typename: 'Mutation', studioEdit: { __typename: 'Edit', id: string, target_type: TargetTypeEnum, operation: OperationEnum, status: VoteStatusEnum, bot: boolean, applied: boolean, created: string, updated?: string | null, closed?: string | null, expires?: string | null, passing?: boolean | null, update_count: number, updatable: boolean, vote_count: number, destructive: boolean, comments: Array<{ __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }>, votes: Array<{ __typename: 'EditVote', date: string, vote: VoteTypeEnum, user?: { __typename: 'User', id: string, name: string } | null }>, user?: { __typename: 'User', id: string, name: string } | null, target?: - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, details?: - | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, image_changes: Array<{ __typename: 'ImageAssignmentChange', added_types: Array, removed_types: Array, date?: string | null, date_changed: boolean, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, old_details?: | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, merge_sources: Array< - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } @@ -3149,22 +3645,22 @@ export type StudioEditUpdateMutationVariables = Exact<{ export type StudioEditUpdateMutation = { __typename: 'Mutation', studioEditUpdate: { __typename: 'Edit', id: string, target_type: TargetTypeEnum, operation: OperationEnum, status: VoteStatusEnum, bot: boolean, applied: boolean, created: string, updated?: string | null, closed?: string | null, expires?: string | null, passing?: boolean | null, update_count: number, updatable: boolean, vote_count: number, destructive: boolean, comments: Array<{ __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }>, votes: Array<{ __typename: 'EditVote', date: string, vote: VoteTypeEnum, user?: { __typename: 'User', id: string, name: string } | null }>, user?: { __typename: 'User', id: string, name: string } | null, target?: - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, details?: - | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, image_changes: Array<{ __typename: 'ImageAssignmentChange', added_types: Array, removed_types: Array, date?: string | null, date_changed: boolean, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, old_details?: | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, merge_sources: Array< - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } @@ -3176,22 +3672,22 @@ export type TagEditMutationVariables = Exact<{ export type TagEditMutation = { __typename: 'Mutation', tagEdit: { __typename: 'Edit', id: string, target_type: TargetTypeEnum, operation: OperationEnum, status: VoteStatusEnum, bot: boolean, applied: boolean, created: string, updated?: string | null, closed?: string | null, expires?: string | null, passing?: boolean | null, update_count: number, updatable: boolean, vote_count: number, destructive: boolean, comments: Array<{ __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }>, votes: Array<{ __typename: 'EditVote', date: string, vote: VoteTypeEnum, user?: { __typename: 'User', id: string, name: string } | null }>, user?: { __typename: 'User', id: string, name: string } | null, target?: - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, details?: - | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, image_changes: Array<{ __typename: 'ImageAssignmentChange', added_types: Array, removed_types: Array, date?: string | null, date_changed: boolean, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, old_details?: | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, merge_sources: Array< - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } @@ -3204,22 +3700,22 @@ export type TagEditUpdateMutationVariables = Exact<{ export type TagEditUpdateMutation = { __typename: 'Mutation', tagEditUpdate: { __typename: 'Edit', id: string, target_type: TargetTypeEnum, operation: OperationEnum, status: VoteStatusEnum, bot: boolean, applied: boolean, created: string, updated?: string | null, closed?: string | null, expires?: string | null, passing?: boolean | null, update_count: number, updatable: boolean, vote_count: number, destructive: boolean, comments: Array<{ __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }>, votes: Array<{ __typename: 'EditVote', date: string, vote: VoteTypeEnum, user?: { __typename: 'User', id: string, name: string } | null }>, user?: { __typename: 'User', id: string, name: string } | null, target?: - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, details?: - | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, image_changes: Array<{ __typename: 'ImageAssignmentChange', added_types: Array, removed_types: Array, date?: string | null, date_changed: boolean, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, old_details?: | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, merge_sources: Array< - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } @@ -3242,6 +3738,20 @@ export type UpdateEditCommentMutationVariables = Exact<{ export type UpdateEditCommentMutation = { __typename: 'Mutation', updateEditComment: { __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null } }; +export type UpdateImageTypeOrderMutationVariables = Exact<{ + input: ImageTypeOrderInput; +}>; + + +export type UpdateImageTypeOrderMutation = { __typename: 'Mutation', imageTypeOrderUpdate: Array<{ __typename: 'ImageTypeGroup', key: ImageTypeGroupEnum, name: string, types: Array<{ __typename: 'ImageType', key: ImageTypeEnum, name: string }> }> }; + +export type UpdateImageTypePreferencesMutationVariables = Exact<{ + input: ImageTypePreferencesInput; +}>; + + +export type UpdateImageTypePreferencesMutation = { __typename: 'Mutation', updateImageTypePreferences: boolean }; + export type UpdateNotificationSubscriptionsMutationVariables = Exact<{ subscriptions: Array | NotificationEnum; }>; @@ -3305,22 +3815,22 @@ export type VoteMutationVariables = Exact<{ export type VoteMutation = { __typename: 'Mutation', editVote: { __typename: 'Edit', id: string, target_type: TargetTypeEnum, operation: OperationEnum, status: VoteStatusEnum, bot: boolean, applied: boolean, created: string, updated?: string | null, closed?: string | null, expires?: string | null, passing?: boolean | null, update_count: number, updatable: boolean, vote_count: number, destructive: boolean, comments: Array<{ __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }>, votes: Array<{ __typename: 'EditVote', date: string, vote: VoteTypeEnum, user?: { __typename: 'User', id: string, name: string } | null }>, user?: { __typename: 'User', id: string, name: string } | null, target?: - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, details?: - | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, image_changes: Array<{ __typename: 'ImageAssignmentChange', added_types: Array, removed_types: Array, date?: string | null, date_changed: boolean, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, old_details?: | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, merge_sources: Array< - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } @@ -3355,7 +3865,7 @@ export type DraftQuery = { __typename: 'Query', findDraft?: { __typename: 'Draft | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, performers: Array< | { __typename: 'DraftEntity', name: string, draftID?: string | null } - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } >, tags?: Array< | { __typename: 'DraftEntity', name: string, draftID?: string | null } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } @@ -3376,22 +3886,22 @@ export type EditQueryVariables = Exact<{ export type EditQuery = { __typename: 'Query', findEdit?: { __typename: 'Edit', id: string, target_type: TargetTypeEnum, operation: OperationEnum, status: VoteStatusEnum, bot: boolean, applied: boolean, created: string, updated?: string | null, closed?: string | null, expires?: string | null, passing?: boolean | null, update_count: number, updatable: boolean, vote_count: number, destructive: boolean, comments: Array<{ __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }>, votes: Array<{ __typename: 'EditVote', date: string, vote: VoteTypeEnum, user?: { __typename: 'User', id: string, name: string } | null }>, user?: { __typename: 'User', id: string, name: string } | null, target?: - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, details?: - | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, image_changes: Array<{ __typename: 'ImageAssignmentChange', added_types: Array, removed_types: Array, date?: string | null, date_changed: boolean, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, old_details?: | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, merge_sources: Array< - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } @@ -3408,13 +3918,13 @@ export type EditUpdateQuery = { __typename: 'Query', findEdit?: { __typename: 'E | { __typename: 'Studio', id: string } | { __typename: 'Tag', id: string } >, options?: { __typename: 'PerformerEditOptions', set_modify_aliases: boolean, set_merge_aliases: boolean } | null, user?: { __typename: 'User', id: string, name: string } | null, target?: - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', aliases: Array, id: string, name: string, description?: string | null, deleted: boolean, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, details?: | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, aliases: Array, draft_id?: string | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, tattoos: Array<{ __typename: 'BodyModification', location: string, description?: string | null }>, piercings: Array<{ __typename: 'BodyModification', location: string, description?: string | null }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> } | { __typename: 'StudioEdit', name?: string | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } | { __typename: 'TagEdit', name?: string | null, description?: string | null, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null } | null }; @@ -3425,22 +3935,22 @@ export type EditsQueryVariables = Exact<{ export type EditsQuery = { __typename: 'Query', queryEdits: { __typename: 'QueryEditsResultType', count: number, edits: Array<{ __typename: 'Edit', id: string, target_type: TargetTypeEnum, operation: OperationEnum, status: VoteStatusEnum, bot: boolean, applied: boolean, created: string, updated?: string | null, closed?: string | null, expires?: string | null, passing?: boolean | null, update_count: number, updatable: boolean, vote_count: number, destructive: boolean, comments: Array<{ __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }>, votes: Array<{ __typename: 'EditVote', date: string, vote: VoteTypeEnum, user?: { __typename: 'User', id: string, name: string } | null }>, user?: { __typename: 'User', id: string, name: string } | null, target?: - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, details?: - | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, image_changes: Array<{ __typename: 'ImageAssignmentChange', added_types: Array, removed_types: Array, date?: string | null, date_changed: boolean, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, old_details?: | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, merge_sources: Array< - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } @@ -3465,7 +3975,15 @@ export type FullPerformerQueryVariables = Exact<{ }>; -export type FullPerformerQuery = { __typename: 'Query', findPerformer?: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, studios: Array<{ __typename: 'PerformerStudio', scene_count: number, studio: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } }>, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } | null }; +export type FullPerformerQuery = { __typename: 'Query', findPerformer?: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, studios: Array<{ __typename: 'PerformerStudio', scene_count: number, studio: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } }>, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | null }; + +export type ImageTypeGroupsQueryVariables = Exact<{ + target?: InputMaybe; + includeDisabled?: InputMaybe; +}>; + + +export type ImageTypeGroupsQuery = { __typename: 'Query', imageTypeGroups: Array<{ __typename: 'ImageTypeGroup', key: ImageTypeGroupEnum, name: string, description?: string | null, enabled: boolean, types: Array<{ __typename: 'ImageType', key: ImageTypeEnum, name: string, description?: string | null, enabled: boolean, conflicts_with: Array, crop_template?: { __typename: 'CropTemplate', aspect_ratio: number, guides: Array<{ __typename: 'CropGuide', axis: CropGuideAxisEnum, position: number, role?: CropGuideRoleEnum | null, label?: string | null, pivot: boolean }>, shapes: Array<{ __typename: 'CropShape', label?: string | null, subpaths: Array<{ __typename: 'CropSubpath', closed: boolean, knots: Array<{ __typename: 'CropKnot', control_in: { __typename: 'CropPoint', x: number, y: number }, anchor: { __typename: 'CropPoint', x: number, y: number }, control_out: { __typename: 'CropPoint', x: number, y: number } }> }> }> } | null }> }> }; export type MeQueryVariables = Exact<{ [key: string]: never; }>; @@ -3502,7 +4020,7 @@ export type PerformerQueryVariables = Exact<{ }>; -export type PerformerQuery = { __typename: 'Query', findPerformer?: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } | null }; +export type PerformerQuery = { __typename: 'Query', findPerformer?: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | null }; export type PerformersQueryVariables = Exact<{ input: PerformerQueryInput; @@ -3523,23 +4041,23 @@ export type QueryExistingPerformerQueryVariables = Exact<{ }>; -export type QueryExistingPerformerQuery = { __typename: 'Query', queryExistingPerformer: { __typename: 'QueryExistingPerformerResult', performers: Array<{ __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> }>, edits: Array<{ __typename: 'Edit', id: string, target_type: TargetTypeEnum, operation: OperationEnum, status: VoteStatusEnum, bot: boolean, applied: boolean, created: string, updated?: string | null, closed?: string | null, expires?: string | null, passing?: boolean | null, update_count: number, updatable: boolean, vote_count: number, destructive: boolean, comments: Array<{ __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }>, votes: Array<{ __typename: 'EditVote', date: string, vote: VoteTypeEnum, user?: { __typename: 'User', id: string, name: string } | null }>, user?: { __typename: 'User', id: string, name: string } | null, target?: - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } +export type QueryExistingPerformerQuery = { __typename: 'Query', queryExistingPerformer: { __typename: 'QueryExistingPerformerResult', performers: Array<{ __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> }>, edits: Array<{ __typename: 'Edit', id: string, target_type: TargetTypeEnum, operation: OperationEnum, status: VoteStatusEnum, bot: boolean, applied: boolean, created: string, updated?: string | null, closed?: string | null, expires?: string | null, passing?: boolean | null, update_count: number, updatable: boolean, vote_count: number, destructive: boolean, comments: Array<{ __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }>, votes: Array<{ __typename: 'EditVote', date: string, vote: VoteTypeEnum, user?: { __typename: 'User', id: string, name: string } | null }>, user?: { __typename: 'User', id: string, name: string } | null, target?: + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, details?: - | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, image_changes: Array<{ __typename: 'ImageAssignmentChange', added_types: Array, removed_types: Array, date?: string | null, date_changed: boolean, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, old_details?: | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, merge_sources: Array< - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } @@ -3551,22 +4069,22 @@ export type QueryExistingSceneQueryVariables = Exact<{ export type QueryExistingSceneQuery = { __typename: 'Query', queryExistingScene: { __typename: 'QueryExistingSceneResult', scenes: Array<{ __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> }>, edits: Array<{ __typename: 'Edit', id: string, target_type: TargetTypeEnum, operation: OperationEnum, status: VoteStatusEnum, bot: boolean, applied: boolean, created: string, updated?: string | null, closed?: string | null, expires?: string | null, passing?: boolean | null, update_count: number, updatable: boolean, vote_count: number, destructive: boolean, comments: Array<{ __typename: 'EditComment', id: string, date: string, comment: string, updated?: string | null, hidden: boolean, user?: { __typename: 'User', id: string, name: string } | null }>, votes: Array<{ __typename: 'EditVote', date: string, vote: VoteTypeEnum, user?: { __typename: 'User', id: string, name: string } | null }>, user?: { __typename: 'User', id: string, name: string } | null, target?: - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, details?: - | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null, draft_id?: string | null, image_changes: Array<{ __typename: 'ImageAssignmentChange', added_types: Array, removed_types: Array, date?: string | null, date_changed: boolean, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }>, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, added_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, removed_piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, draft_id?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, added_aliases?: Array | null, removed_aliases?: Array | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, old_details?: | { __typename: 'PerformerEdit', name?: string | null, disambiguation?: string | null, gender?: GenderEnum | null, birthdate?: string | null, deathdate?: string | null, ethnicity?: EthnicityEnum | null, country?: string | null, eye_color?: EyeColorEnum | null, hair_color?: HairColorEnum | null, height?: number | null, cup_size?: string | null, band_size?: number | null, waist_size?: number | null, hip_size?: number | null, breast_type?: BreastTypeEnum | null, career_start_year?: number | null, career_end_year?: number | null } - | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } + | { __typename: 'SceneEdit', title?: string | null, details?: string | null, date?: string | null, production_date?: string | null, duration?: number | null, director?: string | null, code?: string | null, added_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, removed_urls?: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }> | null, studio?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null, added_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, removed_performers?: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } }> | null, added_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, removed_tags?: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null }> | null, added_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, removed_images?: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> | null, added_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null, removed_fingerprints?: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number }> | null } | { __typename: 'StudioEdit', name?: string | null, parent?: { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | null } | { __typename: 'TagEdit', name?: string | null, description?: string | null, category?: { __typename: 'TagCategory', id: string, name: string } | null } | null, merge_sources: Array< - | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> } + | { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, merged_into_id?: string | null, aliases: Array, gender?: GenderEnum | null, birth_date?: string | null, death_date?: string | null, age?: number | null, height?: number | null, hair_color?: HairColorEnum | null, eye_color?: EyeColorEnum | null, ethnicity?: EthnicityEnum | null, country?: string | null, career_end_year?: number | null, career_start_year?: number | null, breast_type?: BreastTypeEnum | null, waist_size?: number | null, hip_size?: number | null, band_size?: number | null, cup_size?: string | null, is_favorite: boolean, tattoos?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, piercings?: Array<{ __typename: 'BodyModification', location: string, description?: string | null }> | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, typed_images: Array<{ __typename: 'TypedImage', types: Array, date?: string | null, image: { __typename: 'Image', id: string, url: string, width: number, height: number } }> } | { __typename: 'Scene', id: string, release_date?: string | null, production_date?: string | null, title?: string | null, deleted: boolean, details?: string | null, director?: string | null, code?: string | null, duration?: number | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string, parent?: { __typename: 'Studio', id: string, name: string } | null } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array } }>, fingerprints: Array<{ __typename: 'Fingerprint', hash: string, algorithm: FingerprintAlgorithm, duration: number, submissions: number, reports: number, user_submitted: boolean, user_reported: boolean, created: string, updated: string }>, tags: Array<{ __typename: 'Tag', id: string, name: string, description?: string | null, aliases: Array }> } | { __typename: 'Studio', id: string, name: string, aliases: Array, deleted: boolean, is_favorite: boolean, parent?: { __typename: 'Studio', id: string, name: string } | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, height: number, width: number }> } | { __typename: 'Tag', id: string, name: string, description?: string | null, deleted: boolean, aliases: Array, category?: { __typename: 'TagCategory', id: string, name: string } | null } @@ -3795,7 +4313,7 @@ export type SearchAllQueryVariables = Exact<{ }>; -export type SearchAllQuery = { __typename: 'Query', searchPerformers: { __typename: 'QueryPerformersResultType', count: number, performers: Array<{ __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array, country?: string | null, career_start_year?: number | null, career_end_year?: number | null, scene_count: number, birth_date?: string | null, is_favorite: boolean, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> }> }, searchScenes: { __typename: 'QueryScenesResultType', count: number, scenes: Array<{ __typename: 'Scene', id: string, release_date?: string | null, title?: string | null, deleted: boolean, duration?: number | null, code?: string | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, gender?: GenderEnum | null, aliases: Array, deleted: boolean } }> }> } }; +export type SearchAllQuery = { __typename: 'Query', searchPerformers: { __typename: 'QueryPerformersResultType', count: number, performers: Array<{ __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array, country?: string | null, career_start_year?: number | null, career_end_year?: number | null, scene_count: number, birth_date?: string | null, is_favorite: boolean, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, thumbnail?: { __typename: 'Image', url: string } | null }> }, searchScenes: { __typename: 'QueryScenesResultType', count: number, scenes: Array<{ __typename: 'Scene', id: string, release_date?: string | null, title?: string | null, deleted: boolean, duration?: number | null, code?: string | null, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, studio?: { __typename: 'Studio', id: string, name: string } | null, performers: Array<{ __typename: 'PerformerAppearance', as?: string | null, performer: { __typename: 'Performer', id: string, name: string, disambiguation?: string | null, gender?: GenderEnum | null, aliases: Array, deleted: boolean } }> }> } }; export type SearchPerformersQueryVariables = Exact<{ term: Scalars['String']['input']; @@ -3807,7 +4325,7 @@ export type SearchPerformersQueryVariables = Exact<{ }>; -export type SearchPerformersQuery = { __typename: 'Query', searchPerformers: { __typename: 'QueryPerformersResultType', count: number, performers: Array<{ __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array, country?: string | null, career_start_year?: number | null, career_end_year?: number | null, scene_count: number, birth_date?: string | null, is_favorite: boolean, studios?: Array<{ __typename: 'PerformerStudio', scene_count: number }>, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }> }>, facets?: { __typename: 'PerformerSearchFacets', genders: Array<{ __typename: 'GenderFacet', gender: GenderEnum, count: number }> } | null } }; +export type SearchPerformersQuery = { __typename: 'Query', searchPerformers: { __typename: 'QueryPerformersResultType', count: number, performers: Array<{ __typename: 'Performer', id: string, name: string, disambiguation?: string | null, deleted: boolean, gender?: GenderEnum | null, aliases: Array, country?: string | null, career_start_year?: number | null, career_end_year?: number | null, scene_count: number, birth_date?: string | null, is_favorite: boolean, studios?: Array<{ __typename: 'PerformerStudio', scene_count: number }>, urls: Array<{ __typename: 'URL', url: string, site: { __typename: 'Site', id: string, name: string, icon: string, highlighted: boolean, category?: { __typename: 'SiteCategory', id: number, name: string, sort_order: number } | null } }>, images: Array<{ __typename: 'Image', id: string, url: string, width: number, height: number }>, thumbnail?: { __typename: 'Image', url: string } | null }>, facets?: { __typename: 'PerformerSearchFacets', genders: Array<{ __typename: 'GenderFacet', gender: GenderEnum, count: number }> } | null } }; export type SearchScenesQueryVariables = Exact<{ term: Scalars['String']['input']; @@ -3924,7 +4442,7 @@ export type UserQueryVariables = Exact<{ }>; -export type UserQuery = { __typename: 'Query', findUser?: { __typename: 'User', id: string, name: string, email?: string | null, roles?: Array | null, api_key?: string | null, api_calls: number, invite_tokens?: number | null, notification_subscriptions: Array, invited_by?: { __typename: 'User', id: string, name: string } | null, invite_codes?: Array<{ __typename: 'InviteKey', id: string, uses?: number | null, expires?: string | null }> | null, vote_count: { __typename: 'UserVoteCount', accept: number, reject: number, immediate_accept: number, immediate_reject: number, abstain: number }, edit_count: { __typename: 'UserEditCount', immediate_accepted: number, immediate_rejected: number, accepted: number, rejected: number, failed: number, canceled: number, pending: number, immediate_accepted_bot: number, immediate_rejected_bot: number, accepted_bot: number, rejected_bot: number, failed_bot: number, canceled_bot: number, pending_bot: number } } | null }; +export type UserQuery = { __typename: 'Query', findUser?: { __typename: 'User', id: string, name: string, email?: string | null, roles?: Array | null, api_key?: string | null, api_calls: number, invite_tokens?: number | null, notification_subscriptions: Array, image_type_preferences: Array, image_type_group_preferences: Array, invited_by?: { __typename: 'User', id: string, name: string } | null, invite_codes?: Array<{ __typename: 'InviteKey', id: string, uses?: number | null, expires?: string | null }> | null, vote_count: { __typename: 'UserVoteCount', accept: number, reject: number, immediate_accept: number, immediate_reject: number, abstain: number }, edit_count: { __typename: 'UserEditCount', immediate_accepted: number, immediate_rejected: number, accepted: number, rejected: number, failed: number, canceled: number, pending: number, immediate_accepted_bot: number, immediate_rejected_bot: number, accepted_bot: number, rejected_bot: number, failed_bot: number, canceled_bot: number, pending_bot: number } } | null }; export type UsersQueryVariables = Exact<{ input: UserQueryInput; @@ -3942,16 +4460,17 @@ export const CommentFragmentDoc = {"kind":"Document","definitions":[{"kind":"Fra export const TagFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]} as unknown as DocumentNode; export const UrlFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}}]} as unknown as DocumentNode; export const ImageFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}}]} as unknown as DocumentNode; -export const PerformerFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}}]} as unknown as DocumentNode; +export const TypedImageFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}}]} as unknown as DocumentNode; +export const PerformerFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}}]} as unknown as DocumentNode; export const StudioFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}}]} as unknown as DocumentNode; export const ScenePerformerFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]} as unknown as DocumentNode; export const SceneFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]} as unknown as DocumentNode; export const FingerprintFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}}]} as unknown as DocumentNode; -export const EditFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}}]} as unknown as DocumentNode; +export const EditFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image_changes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_types"}},{"kind":"Field","name":{"kind":"Name","value":"removed_types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"date_changed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}}]} as unknown as DocumentNode; export const NotificationSceneFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NotificationSceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}}]} as unknown as DocumentNode; export const PairingSceneFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PairingSceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}}]} as unknown as DocumentNode; export const QuerySceneFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"QuerySceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]} as unknown as DocumentNode; -export const SearchPerformerFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SearchPerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"scene_count"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}}]} as unknown as DocumentNode; +export const SearchPerformerFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SearchPerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"scene_count"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"thumbnail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}}]} as unknown as DocumentNode; export const NotificationEditFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NotificationEditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}}]}}]} as unknown as DocumentNode; export const NotificationCommentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NotificationCommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}},{"kind":"Field","name":{"kind":"Name","value":"edit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationEditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NotificationEditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}}]}}]} as unknown as DocumentNode; export const SearchTagFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SearchTagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]} as unknown as DocumentNode; @@ -3963,8 +4482,8 @@ export const AddSiteCategoryDocument = {"kind":"Document","definitions":[{"kind" export const AddStudioDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"AddStudio"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"studioData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"StudioCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"studioCreate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"studioData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const AddTagCategoryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"AddTagCategory"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"categoryData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"TagCategoryCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"tagCategoryCreate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"categoryData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"group"}}]}}]}}]} as unknown as DocumentNode; export const AddUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"AddUser"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"userData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UserCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userCreate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"userData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"roles"}}]}}]}}]} as unknown as DocumentNode; -export const AmendEditDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"AmendEdit"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AmendEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"amendEdit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; -export const ApproveEditDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ApproveEdit"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ApproveEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"approveEdit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; +export const AmendEditDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"AmendEdit"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AmendEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"amendEdit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image_changes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_types"}},{"kind":"Field","name":{"kind":"Name","value":"removed_types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"date_changed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; +export const ApproveEditDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ApproveEdit"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ApproveEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"approveEdit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image_changes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_types"}},{"kind":"Field","name":{"kind":"Name","value":"removed_types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"date_changed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; export const CancelEditDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CancelEdit"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CancelEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cancelEdit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const ChangePasswordDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ChangePassword"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"userData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UserChangePasswordInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"changePassword"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"userData"}}}]}]}}]} as unknown as DocumentNode; export const ConfirmChangeEmailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ConfirmChangeEmail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"confirmChangeEmail"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}}]}]}}]} as unknown as DocumentNode; @@ -3987,21 +4506,24 @@ export const MarkNotificationReadDocument = {"kind":"Document","definitions":[{" export const MarkNotificationsReadDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MarkNotificationsRead"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"markNotificationsRead"}}]}}]} as unknown as DocumentNode; export const MoveFingerprintSubmissionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MoveFingerprintSubmissions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MoveFingerprintSubmissionsInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sceneMoveFingerprintSubmissions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode; export const NewUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"NewUser"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"NewUserInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"newUser"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode; -export const PerformerEditDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"PerformerEdit"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"performerData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performerEdit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"performerData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; -export const PerformerEditUpdateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"PerformerEditUpdate"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"performerData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performerEditUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"performerData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; +export const PerformerEditDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"PerformerEdit"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"performerData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performerEdit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"performerData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image_changes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_types"}},{"kind":"Field","name":{"kind":"Name","value":"removed_types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"date_changed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; +export const PerformerEditUpdateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"PerformerEditUpdate"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"performerData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performerEditUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"performerData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image_changes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_types"}},{"kind":"Field","name":{"kind":"Name","value":"removed_types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"date_changed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; export const RegenerateApiKeyDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RegenerateAPIKey"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"user_id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"regenerateAPIKey"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"userID"},"value":{"kind":"Variable","name":{"kind":"Name","value":"user_id"}}}]}]}}]} as unknown as DocumentNode; export const RequestChangeEmailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RequestChangeEmail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"requestChangeEmail"}}]}}]} as unknown as DocumentNode; export const RescindInviteCodeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RescindInviteCode"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"code"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"rescindInviteCode"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"code"},"value":{"kind":"Variable","name":{"kind":"Name","value":"code"}}}]}]}}]} as unknown as DocumentNode; export const ResetPasswordDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ResetPassword"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ResetPasswordInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"resetPassword"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode; export const RevokeInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RevokeInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RevokeInviteInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"revokeInvite"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode; -export const SceneEditDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SceneEdit"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sceneData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sceneEdit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sceneData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; -export const SceneEditUpdateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SceneEditUpdate"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sceneData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sceneEditUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sceneData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; -export const StudioEditDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"StudioEdit"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"studioData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"studioEdit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"studioData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; -export const StudioEditUpdateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"StudioEditUpdate"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"studioData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"studioEditUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"studioData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; -export const TagEditDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"TagEdit"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"tagData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"TagEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"tagEdit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"tagData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; -export const TagEditUpdateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"TagEditUpdate"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"tagData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"TagEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"tagEditUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"tagData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; +export const SceneEditDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SceneEdit"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sceneData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sceneEdit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sceneData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image_changes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_types"}},{"kind":"Field","name":{"kind":"Name","value":"removed_types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"date_changed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; +export const SceneEditUpdateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SceneEditUpdate"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sceneData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sceneEditUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sceneData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image_changes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_types"}},{"kind":"Field","name":{"kind":"Name","value":"removed_types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"date_changed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; +export const SetImageTypeEnabledDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SetImageTypeEnabled"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ImageTypeEnabledInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageTypeSetEnabled"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"types"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}}]}}]}}]} as unknown as DocumentNode; +export const StudioEditDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"StudioEdit"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"studioData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"studioEdit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"studioData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image_changes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_types"}},{"kind":"Field","name":{"kind":"Name","value":"removed_types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"date_changed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; +export const StudioEditUpdateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"StudioEditUpdate"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"studioData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"studioEditUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"studioData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image_changes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_types"}},{"kind":"Field","name":{"kind":"Name","value":"removed_types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"date_changed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; +export const TagEditDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"TagEdit"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"tagData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"TagEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"tagEdit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"tagData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image_changes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_types"}},{"kind":"Field","name":{"kind":"Name","value":"removed_types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"date_changed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; +export const TagEditUpdateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"TagEditUpdate"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"tagData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"TagEditInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"tagEditUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"tagData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image_changes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_types"}},{"kind":"Field","name":{"kind":"Name","value":"removed_types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"date_changed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; export const UnmatchFingerprintDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UnmatchFingerprint"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"scene_id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"algorithm"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"FingerprintAlgorithm"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"hash"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"FingerprintHash"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"duration"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"unmatchFingerprint"},"name":{"kind":"Name","value":"submitFingerprint"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"vote"},"value":{"kind":"EnumValue","value":"REMOVE"}},{"kind":"ObjectField","name":{"kind":"Name","value":"scene_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"scene_id"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"fingerprint"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"hash"},"value":{"kind":"Variable","name":{"kind":"Name","value":"hash"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"algorithm"},"value":{"kind":"Variable","name":{"kind":"Name","value":"algorithm"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"duration"},"value":{"kind":"Variable","name":{"kind":"Name","value":"duration"}}}]}}]}}]}]}}]} as unknown as DocumentNode; export const UpdateEditCommentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateEditComment"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateEditCommentInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateEditComment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}}]} as unknown as DocumentNode; +export const UpdateImageTypeOrderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateImageTypeOrder"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ImageTypeOrderInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageTypeOrderUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"types"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; +export const UpdateImageTypePreferencesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateImageTypePreferences"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ImageTypePreferencesInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateImageTypePreferences"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode; export const UpdateNotificationSubscriptionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateNotificationSubscriptions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"subscriptions"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"NotificationEnum"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateNotificationSubscriptions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"subscriptions"},"value":{"kind":"Variable","name":{"kind":"Name","value":"subscriptions"}}}]}]}}]} as unknown as DocumentNode; export const UpdateSceneDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateScene"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"updateData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SceneUpdateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sceneUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"updateData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]}}]}}]} as unknown as DocumentNode; export const UpdateSiteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"siteData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SiteUpdateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"siteUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"siteData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"regex"}},{"kind":"Field","name":{"kind":"Name","value":"valid_types"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}}]}}]}}]} as unknown as DocumentNode; @@ -4010,35 +4532,36 @@ export const UpdateStudioDocument = {"kind":"Document","definitions":[{"kind":"O export const UpdateTagCategoryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateTagCategory"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"categoryData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"TagCategoryUpdateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"tagCategoryUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"categoryData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"group"}}]}}]}}]} as unknown as DocumentNode; export const UpdateUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateUser"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"userData"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UserUpdateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"userData"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"roles"}}]}}]}}]} as unknown as DocumentNode; export const ValidateChangeEmailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ValidateChangeEmail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"email"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"validateChangeEmail"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}},{"kind":"Argument","name":{"kind":"Name","value":"email"},"value":{"kind":"Variable","name":{"kind":"Name","value":"email"}}}]}]}}]} as unknown as DocumentNode; -export const VoteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"Vote"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EditVoteInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"editVote"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; +export const VoteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"Vote"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EditVoteInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"editVote"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image_changes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_types"}},{"kind":"Field","name":{"kind":"Name","value":"removed_types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"date_changed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; export const CategoriesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Categories"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"queryTagCategories"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"tag_categories"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"group"}}]}}]}}]}}]} as unknown as DocumentNode; export const CategoryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Category"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findTagCategory"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"group"}}]}}]}}]} as unknown as DocumentNode; export const ConfigDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Config"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edit_update_limit"}},{"kind":"Field","name":{"kind":"Name","value":"host_url"}},{"kind":"Field","name":{"kind":"Name","value":"require_invite"}},{"kind":"Field","name":{"kind":"Name","value":"require_activation"}},{"kind":"Field","name":{"kind":"Name","value":"vote_promotion_threshold"}},{"kind":"Field","name":{"kind":"Name","value":"vote_application_threshold"}},{"kind":"Field","name":{"kind":"Name","value":"voting_period"}},{"kind":"Field","name":{"kind":"Name","value":"min_destructive_voting_period"}},{"kind":"Field","name":{"kind":"Name","value":"vote_cron_interval"}},{"kind":"Field","name":{"kind":"Name","value":"guidelines_url"}},{"kind":"Field","name":{"kind":"Name","value":"require_scene_draft"}},{"kind":"Field","name":{"kind":"Name","value":"require_tag_role"}}]}}]}}]} as unknown as DocumentNode; -export const DraftDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Draft"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findDraft"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerDraft"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"urls"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"measurements"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"}},{"kind":"Field","name":{"kind":"Name","value":"piercings"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneDraft"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"urls"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"DraftEntity"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"draftID"},"name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"DraftEntity"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"draftID"},"name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"DraftEntity"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"draftID"},"name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]} as unknown as DocumentNode; +export const DraftDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Draft"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findDraft"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerDraft"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"urls"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"measurements"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"}},{"kind":"Field","name":{"kind":"Name","value":"piercings"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneDraft"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"urls"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"DraftEntity"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"draftID"},"name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"DraftEntity"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"draftID"},"name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"DraftEntity"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"draftID"},"name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]} as unknown as DocumentNode; export const DraftsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Drafts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findDrafts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerDraft"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneDraft"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}}]}}]}}]}}]}}]} as unknown as DocumentNode; -export const EditDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Edit"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findEdit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; -export const EditUpdateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"EditUpdate"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findEdit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}}]} as unknown as DocumentNode; -export const EditsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Edits"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EditQueryInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"queryEdits"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"edits"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; +export const EditDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Edit"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findEdit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image_changes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_types"}},{"kind":"Field","name":{"kind":"Name","value":"removed_types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"date_changed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; +export const EditUpdateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"EditUpdate"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findEdit"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}}]} as unknown as DocumentNode; +export const EditsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Edits"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"EditQueryInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"queryEdits"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"edits"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image_changes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_types"}},{"kind":"Field","name":{"kind":"Name","value":"removed_types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"date_changed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; export const FetchSiteFaviconsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FetchSiteFavicons"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"url"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fetchSiteFavicons"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"url"},"value":{"kind":"Variable","name":{"kind":"Name","value":"url"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"image"}}]}}]}}]} as unknown as DocumentNode; export const FingerprintClustersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FingerprintClusters"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"FingerprintClustersInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fingerprintClusters"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"truncated"}},{"kind":"Field","name":{"kind":"Name","value":"clusters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"members"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"scene_submissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"durations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}},{"kind":"Field","name":{"kind":"Name","value":"scene"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"linked_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; -export const FullPerformerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FullPerformer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findPerformer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}},{"kind":"Field","name":{"kind":"Name","value":"studios"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scene_count"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}}]} as unknown as DocumentNode; +export const FullPerformerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FullPerformer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findPerformer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}},{"kind":"Field","name":{"kind":"Name","value":"studios"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scene_count"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}}]} as unknown as DocumentNode; +export const ImageTypeGroupsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ImageTypeGroups"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"target"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ImageTypeScopeEnum"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"includeDisabled"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"imageTypeGroups"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"target"},"value":{"kind":"Variable","name":{"kind":"Name","value":"target"}}},{"kind":"Argument","name":{"kind":"Name","value":"include_disabled"},"value":{"kind":"Variable","name":{"kind":"Name","value":"includeDisabled"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"types"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"conflicts_with"}},{"kind":"Field","name":{"kind":"Name","value":"crop_template"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"aspect_ratio"}},{"kind":"Field","name":{"kind":"Name","value":"guides"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"axis"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"pivot"}}]}},{"kind":"Field","name":{"kind":"Name","value":"shapes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"subpaths"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"knots"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"control_in"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"x"}},{"kind":"Field","name":{"kind":"Name","value":"y"}}]}},{"kind":"Field","name":{"kind":"Name","value":"anchor"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"x"}},{"kind":"Field","name":{"kind":"Name","value":"y"}}]}},{"kind":"Field","name":{"kind":"Name","value":"control_out"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"x"}},{"kind":"Field","name":{"kind":"Name","value":"y"}}]}}]}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const MeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Me"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"me"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"roles"}}]}}]}}]} as unknown as DocumentNode; export const ModAuditsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ModAudits"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ModAuditQueryInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"queryModAudits"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"audits"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"action"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target_id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"data"}},{"kind":"Field","name":{"kind":"Name","value":"reason"}},{"kind":"Field","name":{"kind":"Name","value":"created_at"}}]}}]}}]}}]} as unknown as DocumentNode; export const PairingScenesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PairingScenes"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"performerId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"partnerId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"page"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"per_page"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findPerformer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"partnerId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"queryScenes"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"performers"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"modifier"},"value":{"kind":"EnumValue","value":"INCLUDES_ALL"}},{"kind":"ObjectField","name":{"kind":"Name","value":"value"},"value":{"kind":"ListValue","values":[{"kind":"Variable","name":{"kind":"Name","value":"performerId"}}]}}]}},{"kind":"ObjectField","name":{"kind":"Name","value":"page"},"value":{"kind":"Variable","name":{"kind":"Name","value":"page"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"per_page"},"value":{"kind":"Variable","name":{"kind":"Name","value":"per_page"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"sort"},"value":{"kind":"EnumValue","value":"DATE"}},{"kind":"ObjectField","name":{"kind":"Name","value":"direction"},"value":{"kind":"EnumValue","value":"DESC"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scenes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PairingSceneFragment"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PairingSceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}}]} as unknown as DocumentNode; export const PendingEditsCountDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PendingEditsCount"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"type"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"TargetTypeEnum"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"queryEdits"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"target_type"},"value":{"kind":"Variable","name":{"kind":"Name","value":"type"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"target_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"status"},"value":{"kind":"EnumValue","value":"PENDING"}},{"kind":"ObjectField","name":{"kind":"Name","value":"per_page"},"value":{"kind":"IntValue","value":"1"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}}]}}]}}]} as unknown as DocumentNode; -export const PerformerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Performer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findPerformer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}}]} as unknown as DocumentNode; +export const PerformerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Performer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findPerformer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}}]} as unknown as DocumentNode; export const PerformersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Performers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerQueryInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"queryPerformers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}}]} as unknown as DocumentNode; export const PublicUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PublicUser"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findUser"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"username"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"accept"}},{"kind":"Field","name":{"kind":"Name","value":"reject"}},{"kind":"Field","name":{"kind":"Name","value":"immediate_accept"}},{"kind":"Field","name":{"kind":"Name","value":"immediate_reject"}},{"kind":"Field","name":{"kind":"Name","value":"abstain"}}]}},{"kind":"Field","name":{"kind":"Name","value":"edit_count"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"immediate_accepted"}},{"kind":"Field","name":{"kind":"Name","value":"immediate_rejected"}},{"kind":"Field","name":{"kind":"Name","value":"accepted"}},{"kind":"Field","name":{"kind":"Name","value":"rejected"}},{"kind":"Field","name":{"kind":"Name","value":"failed"}},{"kind":"Field","name":{"kind":"Name","value":"canceled"}},{"kind":"Field","name":{"kind":"Name","value":"pending"}},{"kind":"Field","name":{"kind":"Name","value":"immediate_accepted_bot"}},{"kind":"Field","name":{"kind":"Name","value":"immediate_rejected_bot"}},{"kind":"Field","name":{"kind":"Name","value":"accepted_bot"}},{"kind":"Field","name":{"kind":"Name","value":"rejected_bot"}},{"kind":"Field","name":{"kind":"Name","value":"failed_bot"}},{"kind":"Field","name":{"kind":"Name","value":"canceled_bot"}},{"kind":"Field","name":{"kind":"Name","value":"pending_bot"}}]}}]}}]}}]} as unknown as DocumentNode; -export const QueryExistingPerformerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"QueryExistingPerformer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"QueryExistingPerformerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"queryExistingPerformer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"edits"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; -export const QueryExistingSceneDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"QueryExistingScene"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"QueryExistingSceneInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"queryExistingScene"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scenes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"edits"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; +export const QueryExistingPerformerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"QueryExistingPerformer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"QueryExistingPerformerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"queryExistingPerformer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"edits"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image_changes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_types"}},{"kind":"Field","name":{"kind":"Name","value":"removed_types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"date_changed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; +export const QueryExistingSceneDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"QueryExistingScene"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"QueryExistingSceneInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"queryExistingScene"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scenes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"edits"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EditFragment"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TypedImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TypedImage"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"merged_into_id"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"death_date"}},{"kind":"Field","name":{"kind":"Name","value":"age"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TypedImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StudioFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"width"}}]}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FingerprintFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Fingerprint"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"updatable"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image_changes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_types"}},{"kind":"Field","name":{"kind":"Name","value":"removed_types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"date_changed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"typed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"types"}},{"kind":"Field","name":{"kind":"Name","value":"date"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"added_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tattoos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_piercings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"location"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"added_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"removed_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"draft_id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"old_details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TagEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birthdate"}},{"kind":"Field","name":{"kind":"Name","value":"deathdate"}},{"kind":"Field","name":{"kind":"Name","value":"ethnicity"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"eye_color"}},{"kind":"Field","name":{"kind":"Name","value":"hair_color"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"cup_size"}},{"kind":"Field","name":{"kind":"Name","value":"band_size"}},{"kind":"Field","name":{"kind":"Name","value":"waist_size"}},{"kind":"Field","name":{"kind":"Name","value":"hip_size"}},{"kind":"Field","name":{"kind":"Name","value":"breast_type"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"added_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"as"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"added_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"removed_fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FingerprintFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TagFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PerformerFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"StudioFragment"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_modify_aliases"}},{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}}]}}]} as unknown as DocumentNode; export const NotificationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Notifications"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"QueryNotificationsInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"queryNotifications"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"notifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"read"}},{"kind":"Field","name":{"kind":"Name","value":"level"}},{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FavoritePerformerScene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scene"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationSceneFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FavoritePerformerEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationEditFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FavoriteStudioScene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scene"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationSceneFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FavoriteStudioEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationEditFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CommentOwnEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"comment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationCommentFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CommentCommentedEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"comment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationCommentFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CommentVotedEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"comment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationCommentFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"DownvoteOwnEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationEditFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FailedOwnEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationEditFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"UpdatedEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationEditFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FingerprintedSceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationEditFragment"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FingerprintMovedScene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"source_scene"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationSceneFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target_scene"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationSceneFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprint_hash"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"hidden"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NotificationEditFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Edit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"target_type"}},{"kind":"Field","name":{"kind":"Name","value":"operation"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"bot"}},{"kind":"Field","name":{"kind":"Name","value":"applied"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}},{"kind":"Field","name":{"kind":"Name","value":"closed"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}},{"kind":"Field","name":{"kind":"Name","value":"passing"}},{"kind":"Field","name":{"kind":"Name","value":"update_count"}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"}},{"kind":"Field","name":{"kind":"Name","value":"destructive"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"target"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"merge_sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Studio"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"options"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"set_merge_aliases"}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SceneEdit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"votes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"vote"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NotificationSceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NotificationCommentFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EditComment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CommentFragment"}},{"kind":"Field","name":{"kind":"Name","value":"edit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationEditFragment"}}]}}]}}]} as unknown as DocumentNode; export const SceneDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Scene"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findScene"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SceneFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"production_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"details"}},{"kind":"Field","name":{"kind":"Name","value":"director"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"parent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"reports"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"user_reported"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}}]} as unknown as DocumentNode; export const SceneCountDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SceneCount"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SceneQueryInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"queryScenes"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}}]}}]}}]} as unknown as DocumentNode; export const ScenePairingsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ScenePairings"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"performerId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"names"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"gender"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"GenderFilterEnum"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"favorite"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"page"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},"defaultValue":{"kind":"IntValue","value":"1"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"per_page"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},"defaultValue":{"kind":"IntValue","value":"25"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"direction"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SortDirectionEnum"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sort"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerSortEnum"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fetchScenes"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"scenesPerPage"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},"defaultValue":{"kind":"IntValue","value":"12"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"queryPerformers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"performed_with"},"value":{"kind":"Variable","name":{"kind":"Name","value":"performerId"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"names"},"value":{"kind":"Variable","name":{"kind":"Name","value":"names"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"gender"},"value":{"kind":"Variable","name":{"kind":"Name","value":"gender"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"is_favorite"},"value":{"kind":"Variable","name":{"kind":"Name","value":"favorite"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"page"},"value":{"kind":"Variable","name":{"kind":"Name","value":"page"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"per_page"},"value":{"kind":"Variable","name":{"kind":"Name","value":"per_page"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"direction"},"value":{"kind":"Variable","name":{"kind":"Name","value":"direction"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"sort"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sort"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"queryScenes"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"performers"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"modifier"},"value":{"kind":"EnumValue","value":"INCLUDES_ALL"}},{"kind":"ObjectField","name":{"kind":"Name","value":"value"},"value":{"kind":"ListValue","values":[{"kind":"Variable","name":{"kind":"Name","value":"performerId"}}]}}]}},{"kind":"ObjectField","name":{"kind":"Name","value":"page"},"value":{"kind":"IntValue","value":"1"}},{"kind":"ObjectField","name":{"kind":"Name","value":"per_page"},"value":{"kind":"Variable","name":{"kind":"Name","value":"scenesPerPage"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"sort"},"value":{"kind":"EnumValue","value":"DATE"}},{"kind":"ObjectField","name":{"kind":"Name","value":"direction"},"value":{"kind":"EnumValue","value":"DESC"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"scenes"},"directives":[{"kind":"Directive","name":{"kind":"Name","value":"include"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"if"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fetchScenes"}}}]}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PairingSceneFragment"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PairingSceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}}]}}]} as unknown as DocumentNode; export const ScenesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Scenes"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SceneQueryInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"queryScenes"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scenes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"QuerySceneFragment"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"QuerySceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}}]}}]} as unknown as DocumentNode; export const ScenesWithFingerprintsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ScenesWithFingerprints"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SceneQueryInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"submitted"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"queryScenes"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"scenes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"QuerySceneFragment"}},{"kind":"Field","name":{"kind":"Name","value":"fingerprints"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"is_submitted"},"value":{"kind":"Variable","name":{"kind":"Name","value":"submitted"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"algorithm"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"submissions"}},{"kind":"Field","name":{"kind":"Name","value":"user_submitted"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScenePerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"QuerySceneFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Scene"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScenePerformerFragment"}}]}}]}}]}}]} as unknown as DocumentNode; -export const SearchAllDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SearchAll"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"term"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"5"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"searchPerformers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"term"},"value":{"kind":"Variable","name":{"kind":"Name","value":"term"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SearchPerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"searchScenes"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"term"},"value":{"kind":"Variable","name":{"kind":"Name","value":"term"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"scenes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SearchPerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"scene_count"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}}]} as unknown as DocumentNode; -export const SearchPerformersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SearchPerformers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"term"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"page"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"per_page"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerSearchFilter"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"studioId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"hasStudioId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}},"defaultValue":{"kind":"BooleanValue","value":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"searchPerformers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"term"},"value":{"kind":"Variable","name":{"kind":"Name","value":"term"}}},{"kind":"Argument","name":{"kind":"Name","value":"page"},"value":{"kind":"Variable","name":{"kind":"Name","value":"page"}}},{"kind":"Argument","name":{"kind":"Name","value":"per_page"},"value":{"kind":"Variable","name":{"kind":"Name","value":"per_page"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SearchPerformerFragment"}},{"kind":"Field","name":{"kind":"Name","value":"studios"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"studio_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"studioId"}}}],"directives":[{"kind":"Directive","name":{"kind":"Name","value":"include"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"if"},"value":{"kind":"Variable","name":{"kind":"Name","value":"hasStudioId"}}}]}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scene_count"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"facets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"genders"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SearchPerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"scene_count"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}}]} as unknown as DocumentNode; +export const SearchAllDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SearchAll"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"term"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"5"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"searchPerformers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"term"},"value":{"kind":"Variable","name":{"kind":"Name","value":"term"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SearchPerformerFragment"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"searchScenes"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"term"},"value":{"kind":"Variable","name":{"kind":"Name","value":"term"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"scenes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SearchPerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"scene_count"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"thumbnail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}}]} as unknown as DocumentNode; +export const SearchPerformersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SearchPerformers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"term"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"page"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"per_page"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PerformerSearchFilter"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"studioId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"hasStudioId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}},"defaultValue":{"kind":"BooleanValue","value":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"searchPerformers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"term"},"value":{"kind":"Variable","name":{"kind":"Name","value":"term"}}},{"kind":"Argument","name":{"kind":"Name","value":"page"},"value":{"kind":"Variable","name":{"kind":"Name","value":"page"}}},{"kind":"Argument","name":{"kind":"Name","value":"per_page"},"value":{"kind":"Variable","name":{"kind":"Name","value":"per_page"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SearchPerformerFragment"}},{"kind":"Field","name":{"kind":"Name","value":"studios"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"studio_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"studioId"}}}],"directives":[{"kind":"Directive","name":{"kind":"Name","value":"include"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"if"},"value":{"kind":"Variable","name":{"kind":"Name","value":"hasStudioId"}}}]}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scene_count"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"facets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"genders"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"count"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SearchPerformerFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Performer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"country"}},{"kind":"Field","name":{"kind":"Name","value":"career_start_year"}},{"kind":"Field","name":{"kind":"Name","value":"career_end_year"}},{"kind":"Field","name":{"kind":"Name","value":"scene_count"}},{"kind":"Field","name":{"kind":"Name","value":"birth_date"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"thumbnail"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"Field","name":{"kind":"Name","value":"is_favorite"}}]}}]} as unknown as DocumentNode; export const SearchScenesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SearchScenes"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"term"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"page"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"per_page"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"searchScenes"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"term"},"value":{"kind":"Variable","name":{"kind":"Name","value":"term"}}},{"kind":"Argument","name":{"kind":"Name","value":"page"},"value":{"kind":"Variable","name":{"kind":"Name","value":"page"}}},{"kind":"Argument","name":{"kind":"Name","value":"per_page"},"value":{"kind":"Variable","name":{"kind":"Name","value":"per_page"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"scenes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"release_date"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"urls"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"URLFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ImageFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"studio"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"performers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"as"}},{"kind":"Field","name":{"kind":"Name","value":"performer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"disambiguation"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"URLFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"URL"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"site"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ImageFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Image"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}}]} as unknown as DocumentNode; export const SearchTagsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SearchTags"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"term"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"5"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"exact"},"name":{"kind":"Name","value":"findTagOrAlias"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"term"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SearchTagFragment"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"query"},"name":{"kind":"Name","value":"searchTag"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"term"},"value":{"kind":"Variable","name":{"kind":"Name","value":"term"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SearchTagFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SearchTagFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Tag"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]} as unknown as DocumentNode; export const SiteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Site"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findSite"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"regex"}},{"kind":"Field","name":{"kind":"Name","value":"valid_types"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sort_order"}}]}},{"kind":"Field","name":{"kind":"Name","value":"highlighted"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"created"}},{"kind":"Field","name":{"kind":"Name","value":"updated"}}]}}]}}]} as unknown as DocumentNode; @@ -4053,6 +4576,6 @@ export const SubStudiosDocument = {"kind":"Document","definitions":[{"kind":"Ope export const TagDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Tag"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findTag"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}},{"kind":"Field","name":{"kind":"Name","value":"deleted"}},{"kind":"Field","name":{"kind":"Name","value":"category"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"group"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]}}]}}]} as unknown as DocumentNode; export const TagsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Tags"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"TagQueryInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"queryTags"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"aliases"}}]}}]}}]}}]} as unknown as DocumentNode; export const UnreadNotificationCountDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UnreadNotificationCount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getUnreadNotificationCount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"urgent"}}]}}]}}]} as unknown as DocumentNode; -export const UserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"User"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findUser"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"username"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"roles"}},{"kind":"Field","name":{"kind":"Name","value":"api_key"}},{"kind":"Field","name":{"kind":"Name","value":"api_calls"}},{"kind":"Field","name":{"kind":"Name","value":"invited_by"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"invite_tokens"}},{"kind":"Field","name":{"kind":"Name","value":"invite_codes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"uses"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"accept"}},{"kind":"Field","name":{"kind":"Name","value":"reject"}},{"kind":"Field","name":{"kind":"Name","value":"immediate_accept"}},{"kind":"Field","name":{"kind":"Name","value":"immediate_reject"}},{"kind":"Field","name":{"kind":"Name","value":"abstain"}}]}},{"kind":"Field","name":{"kind":"Name","value":"edit_count"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"immediate_accepted"}},{"kind":"Field","name":{"kind":"Name","value":"immediate_rejected"}},{"kind":"Field","name":{"kind":"Name","value":"accepted"}},{"kind":"Field","name":{"kind":"Name","value":"rejected"}},{"kind":"Field","name":{"kind":"Name","value":"failed"}},{"kind":"Field","name":{"kind":"Name","value":"canceled"}},{"kind":"Field","name":{"kind":"Name","value":"pending"}},{"kind":"Field","name":{"kind":"Name","value":"immediate_accepted_bot"}},{"kind":"Field","name":{"kind":"Name","value":"immediate_rejected_bot"}},{"kind":"Field","name":{"kind":"Name","value":"accepted_bot"}},{"kind":"Field","name":{"kind":"Name","value":"rejected_bot"}},{"kind":"Field","name":{"kind":"Name","value":"failed_bot"}},{"kind":"Field","name":{"kind":"Name","value":"canceled_bot"}},{"kind":"Field","name":{"kind":"Name","value":"pending_bot"}}]}},{"kind":"Field","name":{"kind":"Name","value":"notification_subscriptions"}}]}}]}}]} as unknown as DocumentNode; +export const UserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"User"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findUser"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"username"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"roles"}},{"kind":"Field","name":{"kind":"Name","value":"api_key"}},{"kind":"Field","name":{"kind":"Name","value":"api_calls"}},{"kind":"Field","name":{"kind":"Name","value":"invited_by"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"invite_tokens"}},{"kind":"Field","name":{"kind":"Name","value":"invite_codes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"uses"}},{"kind":"Field","name":{"kind":"Name","value":"expires"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vote_count"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"accept"}},{"kind":"Field","name":{"kind":"Name","value":"reject"}},{"kind":"Field","name":{"kind":"Name","value":"immediate_accept"}},{"kind":"Field","name":{"kind":"Name","value":"immediate_reject"}},{"kind":"Field","name":{"kind":"Name","value":"abstain"}}]}},{"kind":"Field","name":{"kind":"Name","value":"edit_count"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"immediate_accepted"}},{"kind":"Field","name":{"kind":"Name","value":"immediate_rejected"}},{"kind":"Field","name":{"kind":"Name","value":"accepted"}},{"kind":"Field","name":{"kind":"Name","value":"rejected"}},{"kind":"Field","name":{"kind":"Name","value":"failed"}},{"kind":"Field","name":{"kind":"Name","value":"canceled"}},{"kind":"Field","name":{"kind":"Name","value":"pending"}},{"kind":"Field","name":{"kind":"Name","value":"immediate_accepted_bot"}},{"kind":"Field","name":{"kind":"Name","value":"immediate_rejected_bot"}},{"kind":"Field","name":{"kind":"Name","value":"accepted_bot"}},{"kind":"Field","name":{"kind":"Name","value":"rejected_bot"}},{"kind":"Field","name":{"kind":"Name","value":"failed_bot"}},{"kind":"Field","name":{"kind":"Name","value":"canceled_bot"}},{"kind":"Field","name":{"kind":"Name","value":"pending_bot"}}]}},{"kind":"Field","name":{"kind":"Name","value":"notification_subscriptions"}},{"kind":"Field","name":{"kind":"Name","value":"image_type_preferences"}},{"kind":"Field","name":{"kind":"Name","value":"image_type_group_preferences"}}]}}]}}]} as unknown as DocumentNode; export const UsersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Users"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UserQueryInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"queryUsers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"users"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"roles"}},{"kind":"Field","name":{"kind":"Name","value":"api_key"}},{"kind":"Field","name":{"kind":"Name","value":"api_calls"}},{"kind":"Field","name":{"kind":"Name","value":"invited_by"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"invite_tokens"}}]}}]}}]}}]} as unknown as DocumentNode; export const VersionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Version"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hash"}},{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"build_time"}},{"kind":"Field","name":{"kind":"Name","value":"build_type"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/frontend/src/hooks/index.ts b/frontend/src/hooks/index.ts index 6480d53aa..a7fe68453 100644 --- a/frontend/src/hooks/index.ts +++ b/frontend/src/hooks/index.ts @@ -2,6 +2,7 @@ export { default as useAuth } from "./useAuth"; export { useCurrentUser } from "./useCurrentUser"; export { default as useEditFilter } from "./useEditFilter"; export { useEntities } from "./useEntities"; +export { useImageTypeVocabulary } from "./useImageTypeVocabulary"; export { default as usePagination } from "./usePagination"; export { useQueryParams } from "./useQueryParams"; export { useToast } from "./useToast"; diff --git a/frontend/src/hooks/useImageTypeVocabulary.ts b/frontend/src/hooks/useImageTypeVocabulary.ts new file mode 100644 index 000000000..7a3fb1aca --- /dev/null +++ b/frontend/src/hooks/useImageTypeVocabulary.ts @@ -0,0 +1,68 @@ +import { useMemo } from "react"; +import type { CropTemplateInfo } from "src/components/cropFrame"; +import { useImageTypeGroups } from "src/graphql"; + +/** + * The two things a component reading someone else's labels needs: the name to + * print, and the frame the labels claim. + * + * Both come off one vocabulary query, so a component that wants both does not + * ask twice. Apollo caches per set of variables, so every caller of this hook + * shares one request -- but not with a caller passing a target or + * include_disabled, which are different entries. + * + * For components displaying an image someone else labelled: a gallery, an edit + * diff. The editor holds the vocabulary already and reads names straight out + * of it rather than calling this. + */ +export const useImageTypeVocabulary = () => { + const { data } = useImageTypeGroups({}); + + return useMemo(() => { + const types = (data?.imageTypeGroups ?? []).flatMap((group) => group.types); + + const names = new Map( + types.map((type) => [type.key as string, type.name] as const), + ); + + const descriptions = new Map( + types.flatMap((type) => + type.description + ? [[type.key as string, type.description] as const] + : [], + ), + ); + + const templates = new Map( + types.flatMap((type) => + type.crop_template + ? [ + [ + type.key as string, + { + aspectRatio: type.crop_template.aspect_ratio, + guides: type.crop_template.guides, + shapes: type.crop_template.shapes, + }, + ] as const, + ] + : [], + ), + ); + + return { + // Falls back to the key while the query is in flight, and for a type the + // instance has switched off, which this deliberately does not ask for. + typeName: (key: string) => names.get(key) ?? key, + + // Undefined rather than the key: a tooltip repeating the label it hangs + // off is worse than no tooltip. + typeDescription: (key: string) => descriptions.get(key), + + // At most one applies: crops are an exclusive group, so an image cannot + // claim two frames and the first match is the only match. + templateFor: (keys: string[]): CropTemplateInfo | undefined => + keys.map((key) => templates.get(key)).find(Boolean), + }; + }, [data]); +}; diff --git a/frontend/src/pages/drafts/parse.ts b/frontend/src/pages/drafts/parse.ts index f8be7fa0a..b67df3c83 100644 --- a/frontend/src/pages/drafts/parse.ts +++ b/frontend/src/pages/drafts/parse.ts @@ -1,4 +1,5 @@ import { uniqBy } from "lodash-es"; +import { toTypedImages } from "src/components/editImages"; import { BreastTypeEnum, type DraftQuery, @@ -130,7 +131,9 @@ export const parseSceneDraft = ( director: draft.director, code: draft.code, duration: draft.fingerprints?.[0]?.duration ?? null, - images: draft.image ? [draft.image] : existingScene?.images, + images: toTypedImages( + draft.image ? [draft.image] : (existingScene?.images ?? []), + ), tags: joinTags( (draft.tags ?? []).reduce((res, t) => { if (t.__typename === "Tag") res.push(t); @@ -240,7 +243,12 @@ export const parsePerformerDraft = ( const performer: InitialPerformer = { name: draft.name, disambiguation: draft.disambiguation ?? null, - images: joinImages(draft.image, existingPerformer?.images), + images: toTypedImages( + joinImages( + draft.image, + existingPerformer?.typed_images.map((typed) => typed.image), + ), + ), gender: parseEnum(draft.gender, GenderEnum), ethnicity: parseEnum(draft.ethnicity, EthnicityEnum), eye_color: parseEnum(draft.eye_color, EyeColorEnum), diff --git a/frontend/src/pages/imageTypes/ImageTypeOrder.tsx b/frontend/src/pages/imageTypes/ImageTypeOrder.tsx new file mode 100644 index 000000000..c200d016c --- /dev/null +++ b/frontend/src/pages/imageTypes/ImageTypeOrder.tsx @@ -0,0 +1,243 @@ +import cx from "classnames"; +import { type FC, useEffect, useState } from "react"; +import { Button, Card, Form } from "react-bootstrap"; +import { DragList } from "src/components/dragList"; +import { ErrorMessage, LoadingIndicator } from "src/components/fragments"; +import Modal from "src/components/modal"; +import Title from "src/components/title"; +import { + type ImageTypeEnum, + type ImageTypeGroupEnum, + useImageTypeGroups, + useSetImageTypeEnabled, + useUpdateImageTypeOrder, +} from "src/graphql"; +import { useCurrentUser } from "src/hooks"; + +const CLASSNAME = "ImageTypeOrder"; + +interface OrderedGroup { + key: ImageTypeGroupEnum; + name: string; + description?: string | null; + enabled: boolean; + types: { + key: ImageTypeEnum; + name: string; + description?: string | null; + enabled: boolean; + }[]; +} + +const ImageTypeOrder: FC = () => { + const { isAdmin } = useCurrentUser(); + const { loading, data } = useImageTypeGroups({ includeDisabled: true }); + const [updateOrder, { loading: savingOrder }] = useUpdateImageTypeOrder(); + const [setEnabled, { loading: savingEnabled }] = useSetImageTypeEnabled(); + const saving = savingOrder || savingEnabled; + + const [groups, setGroups] = useState([]); + const [saved, setSaved] = useState(false); + const [error, setError] = useState(); + const [confirming, setConfirming] = useState(false); + + useEffect(() => { + if (data?.imageTypeGroups) setGroups(data.imageTypeGroups); + }, [data]); + + if (!isAdmin) return ; + if (loading) return ; + + const reorderGroups = (next: OrderedGroup[]) => { + setSaved(false); + setGroups(next); + }; + + const reorderTypes = (groupIndex: number, types: OrderedGroup["types"]) => { + setSaved(false); + setGroups((current) => + current.map((group, i) => + i === groupIndex ? { ...group, types } : group, + ), + ); + }; + + const toggleGroup = (key: ImageTypeGroupEnum) => { + setSaved(false); + setGroups((current) => + current.map((group) => + group.key === key ? { ...group, enabled: !group.enabled } : group, + ), + ); + }; + + const toggleType = (key: ImageTypeEnum) => { + setSaved(false); + setGroups((current) => + current.map((group) => ({ + ...group, + types: group.types.map((type) => + type.key === key ? { ...type, enabled: !type.enabled } : type, + ), + })), + ); + }; + + const save = () => { + setConfirming(false); + setError(undefined); + + updateOrder({ + variables: { + input: { + // Both lists go complete: a partial ordering is rejected rather than + // merged so an admin cannot half-apply a reordering + groups: groups.map((group) => group.key), + types: groups.flatMap((group) => group.types.map((type) => type.key)), + }, + }, + }) + .then(() => + setEnabled({ + variables: { + input: { + disabled_groups: groups + .filter((group) => !group.enabled) + .map((group) => group.key), + disabled_types: groups.flatMap((group) => + group.types + .filter((type) => !type.enabled) + .map((type) => type.key), + ), + }, + }, + }), + ) + .then(() => setSaved(true)) + .catch((e: unknown) => + setError(e instanceof Error ? e.message : String(e)), + ); + }; + + return ( + <> + + <div className="d-flex align-items-center mb-2"> + <h3 className="me-4">Image Types</h3> + <Button + onClick={() => setConfirming(true)} + disabled={saving} + className="ms-auto" + > + {saving ? "Saving..." : "Save Order"} + </Button> + </div> + + <p className="text-muted"> + Which of the vocabulary this instance uses, and which image ranks first{" "} + <b>instance-wide</b>. Groups are compared in this order, and within a + group its types are. Drag by the handles to reorder, or focus a handle + and use the arrow keys. Users may reorder all of this for themselves: + what you set here is the default they start from + </p> + <p className="text-muted"> + Switching something off hides it when labelling and drops it from + ranking. <b>Nothing is deleted</b>: labels already applied are kept and + come back if you switch it on again. + </p> + + {error && <ErrorMessage error={error} />} + {saved && <div className="text-success mb-2">Order saved.</div>} + + <div className={CLASSNAME}> + <DragList + className="is-block" + items={groups} + keyOf={(group) => group.key} + labelOf={(group) => `the ${group.name} group`} + onReorder={reorderGroups} + > + {(group) => ( + <Card className={cx("mb-3", { "is-disabled": !group.enabled })}> + <Card.Header className="d-flex align-items-center"> + <b>{group.name}</b> + {group.description && ( + <small className="text-muted ms-2">{group.description}</small> + )} + <Form.Switch + className="ms-auto" + id={`enabled-${group.key}`} + label={group.enabled ? "In use" : "Off"} + checked={group.enabled} + onChange={() => toggleGroup(group.key)} + /> + </Card.Header> + <Card.Body> + <DragList + items={group.types} + keyOf={(type) => type.key} + labelOf={(type) => type.name} + onReorder={(types) => + reorderTypes( + groups.findIndex((g) => g.key === group.key), + types, + ) + } + > + {(type) => ( + <> + <span + className={cx({ + // A type inside a switched-off group is unusable + // whatever its own setting, so it reads as off + "text-muted": !type.enabled || !group.enabled, + })} + > + {type.name} + </span> + {type.description && ( + <small className="text-muted">{type.description}</small> + )} + <Form.Switch + className="ms-auto" + id={`enabled-${type.key}`} + aria-label={`Use ${type.name}`} + checked={type.enabled} + disabled={!group.enabled} + title={ + group.enabled + ? undefined + : `${group.name} is switched off, so none of its types are in use` + } + onChange={() => toggleType(type.key)} + /> + </> + )} + </DragList> + </Card.Body> + </Card> + )} + </DragList> + </div> + + {confirming && ( + <Modal + acceptTerm="Save for everyone" + cancelTerm="Cancel" + callback={(confirmed) => (confirmed ? save() : setConfirming(false))} + > + <p> + This changes the ranking for <b>every user on this instance</b>, not + just you. + </p> + <p className="mb-0"> + To change only your own ordering, use <b>Image preferences</b> on + your user page instead. + </p> + </Modal> + )} + </> + ); +}; + +export default ImageTypeOrder; diff --git a/frontend/src/pages/imageTypes/index.ts b/frontend/src/pages/imageTypes/index.ts new file mode 100644 index 000000000..a1dc3f665 --- /dev/null +++ b/frontend/src/pages/imageTypes/index.ts @@ -0,0 +1 @@ +export { default } from "./ImageTypeOrder"; diff --git a/frontend/src/pages/imageTypes/styles.scss b/frontend/src/pages/imageTypes/styles.scss new file mode 100644 index 000000000..1f3d94e9a --- /dev/null +++ b/frontend/src/pages/imageTypes/styles.scss @@ -0,0 +1,5 @@ +.ImageTypeOrder { + .card.is-disabled { + opacity: 0.55; + } +} diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx index b1fe2674d..e1ec94953 100644 --- a/frontend/src/pages/index.tsx +++ b/frontend/src/pages/index.tsx @@ -9,6 +9,7 @@ import { ROUTE_EDITS, ROUTE_FORGOT_PASSWORD, ROUTE_HOME, + ROUTE_IMAGE_TYPES, ROUTE_LOGIN, ROUTE_NOTIFICATIONS, ROUTE_PERFORMERS, @@ -31,6 +32,7 @@ import Drafts from "src/pages/drafts"; import Edits from "src/pages/edits"; import ForgotPassword from "src/pages/forgotPassword"; import Home from "src/pages/home"; +import ImageTypes from "src/pages/imageTypes"; import Notifications from "src/pages/notifications"; import Performers from "src/pages/performers"; import RegisterUser from "src/pages/registerUser"; @@ -71,6 +73,7 @@ const Pages: FC = () => ( path={`${ROUTE_SITE_CATEGORIES}/*`} element={<SiteCategories />} /> + <Route path={ROUTE_IMAGE_TYPES} element={<ImageTypes />} /> <Route path={`${ROUTE_DRAFTS}/*`} element={<Drafts />} /> <Route path={ROUTE_NOTIFICATIONS} element={<Notifications />} /> <Route path={`${ROUTE_AUDITS}/*`} element={<Audits />} /> diff --git a/frontend/src/pages/performers/PerformerEditUpdate.tsx b/frontend/src/pages/performers/PerformerEditUpdate.tsx index eea7badfe..d59e3628b 100644 --- a/frontend/src/pages/performers/PerformerEditUpdate.tsx +++ b/frontend/src/pages/performers/PerformerEditUpdate.tsx @@ -1,5 +1,6 @@ import { type FC, useState } from "react"; import { useNavigate } from "react-router-dom"; +import { toTypedImages } from "src/components/editImages"; import { type EditUpdateQuery, @@ -77,7 +78,10 @@ export const PerformerEditUpdate: FC<{ edit: EditUpdate }> = ({ edit }) => { <hr /> <PerformerForm performer={edit.target} - initial={edit.details} + initial={{ + ...edit.details, + images: toTypedImages(edit.details.images), + }} options={edit.options} callback={doUpdate} saving={saving} diff --git a/frontend/src/pages/performers/PerformerMerge.tsx b/frontend/src/pages/performers/PerformerMerge.tsx index bb9c86d48..82f8d76a9 100644 --- a/frontend/src/pages/performers/PerformerMerge.tsx +++ b/frontend/src/pages/performers/PerformerMerge.tsx @@ -134,7 +134,13 @@ const PerformerMerge: FC<Props> = ({ performer }) => { <Row> <Col xs={3}> <h6 className="text-center">Merge Target</h6> - <PerformerCard performer={performer} className="TargetCard" /> + <PerformerCard + performer={{ + ...performer, + images: performer.typed_images.map((typed) => typed.image), + }} + className="TargetCard" + /> </Col> <Col xs={9}> <Row className="mt-4"> diff --git a/frontend/src/pages/performers/components/performerInfo.tsx b/frontend/src/pages/performers/components/performerInfo.tsx index eb3541c41..340789aed 100644 --- a/frontend/src/pages/performers/components/performerInfo.tsx +++ b/frontend/src/pages/performers/components/performerInfo.tsx @@ -1,5 +1,5 @@ import { faCodeMerge } from "@fortawesome/free-solid-svg-icons"; -import type { FC } from "react"; +import { type FC, useMemo } from "react"; import { Button, Card, Col, Row, Table } from "react-bootstrap"; import { Link } from "react-router-dom"; import { @@ -28,7 +28,7 @@ import { type PerformerFragment as Performer, usePerformer, } from "src/graphql"; -import { useCurrentUser } from "src/hooks"; +import { useCurrentUser, useImageTypeVocabulary } from "src/hooks"; import { createHref, formatBodyModifications, @@ -40,6 +40,28 @@ import { const CLASSNAME = "PerformerInfo"; const CLASSNAME_ACTIONS = "PerformerInfo-actions"; +const useImageLabels = (typedImages: Performer["typed_images"]) => { + const { typeName } = useImageTypeVocabulary(); + + return useMemo( + () => + Object.fromEntries( + typedImages + .filter( + (typedImage) => typedImage.types.length > 0 || typedImage.date, + ) + .map((typedImage) => [ + typedImage.image.id, + [ + ...typedImage.types.map(typeName), + ...(typedImage.date ? [typedImage.date] : []), + ], + ]), + ), + [typedImages, typeName], + ); +}; + interface Props { performer: Performer; } @@ -92,6 +114,7 @@ export const PerformerInfo: FC<Props> = ({ performer }) => { { id: performer.merged_into_id ?? "" }, !performer.merged_into_id, ); + const labels = useImageLabels(performer.typed_images); return ( <div className={CLASSNAME}> @@ -222,10 +245,11 @@ export const PerformerInfo: FC<Props> = ({ performer }) => { </Col> <Col xs={6} className="performer-photo"> <Image - images={performer.images} + images={performer.typed_images.map((typed) => typed.image)} size={600} alt="Performer" lightbox + lightboxProps={{ labels }} /> </Col> </Row> diff --git a/frontend/src/pages/performers/performerForm/PerformerForm.tsx b/frontend/src/pages/performers/performerForm/PerformerForm.tsx index 5a297acf3..6c2b4ee95 100644 --- a/frontend/src/pages/performers/performerForm/PerformerForm.tsx +++ b/frontend/src/pages/performers/performerForm/PerformerForm.tsx @@ -11,7 +11,7 @@ import { Controller, useForm } from "react-hook-form"; import { Link } from "react-router-dom"; import Select from "react-select"; import { renderPerformerDetails } from "src/components/editCard/ModifyEdit"; -import EditImages from "src/components/editImages"; +import EditImages, { type TypedImage } from "src/components/editImages"; import { BodyModification, EditNote, @@ -29,7 +29,7 @@ import { EyeColorEnum, GenderEnum, HairColorEnum, - type ImageFragment, + ImageTypeScopeEnum, type PerformerFragment as Performer, type PerformerEditDetailsInput, type PerformerEditOptionsInput, @@ -185,7 +185,7 @@ const PerformerForm: FC<PerformerProps> = ({ career_end_year: initial?.career_end_year ?? performer?.career_end_year, tattoos: initial?.tattoos ?? performer?.tattoos ?? [], piercings: initial?.piercings ?? performer?.piercings ?? [], - images: initial?.images ?? performer?.images ?? [], + images: initial?.images ?? performer?.typed_images ?? [], urls: initial?.urls ?? performer?.urls ?? [], }, }); @@ -254,7 +254,13 @@ const PerformerForm: FC<PerformerProps> = ({ tattoos: data.tattoos ?? [], breast_type: BreastTypeEnum[data.breastType as keyof typeof BreastTypeEnum] || null, - image_ids: data.images.map((i) => i.id), + image_ids: data.images.map((i) => i.image.id), + // Sent for every image, so clearing an image's labels is expressible + image_types: data.images.map((i) => ({ + image_id: i.image.id, + types: i.types, + date: i.date || null, + })), urls: data.urls?.map((u) => ({ url: u.url, site_id: u.site.id, @@ -697,10 +703,11 @@ const PerformerForm: FC<PerformerProps> = ({ <Tab eventKey="images" title="Images"> <EditImages - lens={lens.focus("images").cast<ImageFragment[]>()} + lens={lens.focus("images").cast<TypedImage[]>()} file={file} setFile={(f) => setFile(f)} - original={performer?.images} + original={performer?.typed_images} + target={ImageTypeScopeEnum.PERFORMER} /> <NavButtons diff --git a/frontend/src/pages/performers/performerForm/__tests__/PerformerForm.test.tsx b/frontend/src/pages/performers/performerForm/__tests__/PerformerForm.test.tsx index 9ba796787..d65256e6b 100644 --- a/frontend/src/pages/performers/performerForm/__tests__/PerformerForm.test.tsx +++ b/frontend/src/pages/performers/performerForm/__tests__/PerformerForm.test.tsx @@ -5,6 +5,7 @@ import { EyeColorEnum, GenderEnum, HairColorEnum, + ImageTypeEnum, type PerformerFragment, } from "src/graphql"; import { configMock, sitesMock } from "src/test/graphqlMocks"; @@ -19,9 +20,15 @@ import PerformerForm from "../PerformerForm"; // EditImages can't be exercised in jsdom (file uploads), and the read-only // summary on the Confirm tab isn't useful for these tests. -vi.mock("src/components/editImages", () => ({ - default: () => <div data-testid="edit-images" />, -})); +// Only the component is stubbed: toTypedImages is a pure helper the form +// needs for its initial values +vi.mock("src/components/editImages", async (orig) => { + const real = (await orig()) as typeof import("src/components/editImages"); + return { + ...real, + default: () => <div data-testid="edit-images" />, + }; +}); vi.mock("src/components/editCard/ModifyEdit", async (orig) => { const real = (await orig()) as typeof import("src/components/editCard/ModifyEdit"); @@ -66,6 +73,7 @@ const fullPerformer: PerformerFragment = { }, ], images: [], + typed_images: [], tattoos: [{ location: "arm", description: "rose" }], piercings: [{ location: "ear", description: null }], } as unknown as PerformerFragment; @@ -264,6 +272,41 @@ describe("PerformerForm", () => { }); describe("modify", () => { + // EditImages is stubbed here, so this covers the payload rather than the + // selector: labels a performer already carries must survive an edit that + // does not touch them, and reach the mutation as image_types + it("carries existing image labels and dates into the payload", async () => { + const labelled = { + ...fullPerformer, + typed_images: [ + { + image: { id: "img-1", url: "u", width: 400, height: 600 }, + types: [ImageTypeEnum.SHOT_PORTRAIT, ImageTypeEnum.CROP_FACE], + date: "2019-06", + }, + ], + } as unknown as PerformerFragment; + + const callback = vi.fn(); + const { user } = renderEdit(callback, labelled); + const name = screen.getByLabelText("Name"); + await user.clear(name); + await user.type(name, "Janet Doe"); + await submit(user); + + await waitFor(() => expect(callback).toHaveBeenCalledTimes(1)); + expect(lastCallback(callback)).toMatchObject({ + image_ids: ["img-1"], + image_types: [ + { + image_id: "img-1", + types: [ImageTypeEnum.SHOT_PORTRAIT, ImageTypeEnum.CROP_FACE], + date: "2019-06", + }, + ], + }); + }); + it("changes name", async () => { const callback = vi.fn(); const { user } = renderEdit(callback); diff --git a/frontend/src/pages/performers/performerForm/__tests__/diff.test.ts b/frontend/src/pages/performers/performerForm/__tests__/diff.test.ts index 51032a851..402fa2752 100644 --- a/frontend/src/pages/performers/performerForm/__tests__/diff.test.ts +++ b/frontend/src/pages/performers/performerForm/__tests__/diff.test.ts @@ -23,6 +23,13 @@ const image = (id: string) => ({ height: 100, }); +// The form carries images wrapped with their labels so the diff unwraps them +const typedImage = (id: string) => ({ + image: image(id), + types: [], + date: null, +}); + const basePerformer = ( overrides: Partial<PerformerFragment> = {}, ): PerformerFragment => @@ -47,7 +54,7 @@ const basePerformer = ( hair_color: HairColorEnum.BLONDE, aliases: ["JD"], urls: [{ url: "https://a", site: site("1") }], - images: [image("img-1")], + typed_images: [typedImage("img-1")], tattoos: [{ location: "arm", description: "rose" }], piercings: [{ location: "ear", description: null }], ...overrides, @@ -76,7 +83,7 @@ const baseFormData = ( hair_color: HairColorEnum.BLONDE, aliases: ["JD"], urls: [{ url: "https://a", site: site("1") }], - images: [image("img-1")], + images: [typedImage("img-1")], tattoos: [{ location: "arm", description: "rose" }], piercings: [{ location: "ear", description: null }], note: "n", @@ -214,7 +221,7 @@ describe("selectPerformerDetails", () => { it("diffs images add/remove", () => { const [, neu] = selectPerformerDetails( - baseFormData({ images: [image("img-2")] }), + baseFormData({ images: [typedImage("img-2")] }), basePerformer(), ); expect(neu.added_images).toEqual([image("img-2")]); diff --git a/frontend/src/pages/performers/performerForm/__tests__/merge.test.ts b/frontend/src/pages/performers/performerForm/__tests__/merge.test.ts index ec8435a8d..04d74277b 100644 --- a/frontend/src/pages/performers/performerForm/__tests__/merge.test.ts +++ b/frontend/src/pages/performers/performerForm/__tests__/merge.test.ts @@ -4,6 +4,7 @@ import { EyeColorEnum, GenderEnum, HairColorEnum, + ImageTypeEnum, type PerformerFragment, } from "src/graphql/types"; import { describe, expect, it } from "vitest"; @@ -22,6 +23,13 @@ const image = (id: string) => ({ height: 100, }); +// The merge prefill reads typed_images, since that is what carries labels +const typedImage = (id: string, ...types: ImageTypeEnum[]) => ({ + image: image(id), + types, + date: null, +}); + const performer = ( id: string, overrides: Record<string, unknown> = {}, @@ -48,6 +56,7 @@ const performer = ( aliases: [], urls: [], images: [], + typed_images: [], tattoos: [], piercings: [], ...overrides, @@ -84,14 +93,17 @@ describe("buildPerformerMerge", () => { const target = performer("target", { name: "Jane", aliases: ["JD"], - images: [image("a")], + typed_images: [typedImage("a", ImageTypeEnum.CROP_FACE)], urls: [{ url: "https://x", site: site("1") }], tattoos: [{ location: "arm", description: "rose" }], }); const source = performer("source", { name: "Janie", aliases: ["JD", "J"], - images: [image("a"), image("b")], + typed_images: [ + typedImage("a", ImageTypeEnum.CROP_FACE), + typedImage("b", ImageTypeEnum.CROP_WIDE), + ], urls: [{ url: "https://x", site: site("1") }], tattoos: [ { location: "arm", description: "rose" }, @@ -103,7 +115,13 @@ describe("buildPerformerMerge", () => { // Source name becomes an alias, deduped, target name excluded. expect(initial.aliases).toEqual(["JD", "Janie", "J"]); - expect(initial.images?.map((i) => i.id)).toEqual(["a", "b"]); + expect(initial.images?.map((i) => i.image.id)).toEqual(["a", "b"]); + // The prefill is the whole merge-union mechanism: nothing unions labels + // at apply time, so a source label reaches the target only via the form + expect(initial.images?.map((i) => i.types)).toEqual([ + [ImageTypeEnum.CROP_FACE], + [ImageTypeEnum.CROP_WIDE], + ]); expect(initial.urls).toHaveLength(1); expect(initial.tattoos).toHaveLength(2); }); diff --git a/frontend/src/pages/performers/performerForm/diff.ts b/frontend/src/pages/performers/performerForm/diff.ts index 6c64ca853..22eeaf576 100644 --- a/frontend/src/pages/performers/performerForm/diff.ts +++ b/frontend/src/pages/performers/performerForm/diff.ts @@ -7,6 +7,7 @@ import type { PerformerFragment } from "src/graphql"; import { breastType, diffArray, + diffImageLabels, diffImages, diffURLs, diffValue, @@ -43,9 +44,20 @@ const selectPerformerDetails = ( Required<Omit<PerformerDetails, "draft_id">>, ] => { const [addedImages, removedImages] = diffImages( + data.images.map((i) => i.image), + original?.typed_images.map((typed) => typed.image) ?? [], + ); + const imageChanges = diffImageLabels( data.images, - original?.images ?? [], + original?.typed_images ?? [], ); + // Straight off form state rather than diffed: here the preview *is* the + // submission so there is nothing to reconcile + const typedImages = data.images.map((entry) => ({ + image: entry.image, + types: entry.types, + date: entry.date ?? null, + })); const [addedUrls, removedUrls] = diffURLs(data.urls, original?.urls ?? []); const [addedTattoos, removedTattoos] = diffBodyMods( data.tattoos, @@ -127,6 +139,8 @@ const selectPerformerDetails = ( removed_aliases: removedAliases, added_images: addedImages, removed_images: removedImages, + image_changes: imageChanges, + typed_images: typedImages, added_urls: addedUrls, removed_urls: removedUrls, }, diff --git a/frontend/src/pages/performers/performerForm/merge.ts b/frontend/src/pages/performers/performerForm/merge.ts index e27e88314..71b32414e 100644 --- a/frontend/src/pages/performers/performerForm/merge.ts +++ b/frontend/src/pages/performers/performerForm/merge.ts @@ -201,9 +201,13 @@ export const buildPerformerMerge = ( ...sources.flatMap((p) => p.aliases), ].filter((name) => name !== target.name.trim()), ); + // Labels and dates come from typed_images, not images. This prefill is the + // whole merge-union mechanism: nothing unions assignments at apply time, so + // a source's labels reach the target only by travelling in the submitted + // input. Dropping to bare images here would silently strip them. initial.images = uniqBy( - all.flatMap((p) => p.images), - (image) => image.id, + all.flatMap((p) => p.typed_images), + (typedImage) => typedImage.image.id, ); initial.urls = uniqBy( all.flatMap((p) => p.urls), diff --git a/frontend/src/pages/performers/performerForm/schema.ts b/frontend/src/pages/performers/performerForm/schema.ts index 532fa0c4b..5f5d38583 100644 --- a/frontend/src/pages/performers/performerForm/schema.ts +++ b/frontend/src/pages/performers/performerForm/schema.ts @@ -1,3 +1,4 @@ +import type { ImageTypeEnum } from "src/graphql"; import { BreastTypeEnum, EthnicityEnum, @@ -6,10 +7,10 @@ import { HairColorEnum, } from "src/graphql"; import { - isDateInRange, - isValidDate, maxBirthdate, maxDeathdate, + maxImageDate, + partialDateSchema, } from "src/utils"; import * as yup from "yup"; @@ -27,32 +28,8 @@ export const PerformerSchema = yup.object({ .nullable() .oneOf([null, ...Object.keys(GenderEnum)], "Gender is required"), disambiguation: yup.string().trim().transform(nullCheck).nullable(), - birthdate: yup - .string() - .trim() - .transform(nullCheck) - .matches(/^\d{4}$|^\d{4}-\d{2}$|^\d{4}-\d{2}-\d{2}$/, { - excludeEmptyString: true, - message: "Invalid date, must be YYYY, YYYY-MM, or YYYY-MM-DD", - }) - .test("valid-date", "Invalid date", isValidDate) - .test("date-outside-range", "Outside of range", (date) => - isDateInRange(date, maxBirthdate()), - ) - .nullable(), - deathdate: yup - .string() - .trim() - .transform(nullCheck) - .matches(/^\d{4}$|^\d{4}-\d{2}$|^\d{4}-\d{2}-\d{2}$/, { - excludeEmptyString: true, - message: "Invalid date, must be YYYY, YYYY-MM, or YYYY-MM-DD", - }) - .test("valid-date", "Invalid date", isValidDate) - .test("date-outside-range", "Outside of range", (date) => - isDateInRange(date, maxDeathdate()), - ) - .nullable(), + birthdate: partialDateSchema(maxBirthdate()), + deathdate: partialDateSchema(maxDeathdate()), career_start_year: yup .number() .transform(zeroCheck) @@ -135,10 +112,18 @@ export const PerformerSchema = yup.object({ .array() .of( yup.object({ - id: yup.string().required(), - url: yup.string().required(), - width: yup.number().default(0), - height: yup.number().default(0), + image: yup.object({ + id: yup.string().required(), + url: yup.string().required(), + width: yup.number().default(0), + height: yup.number().default(0), + }), + types: yup + .array() + .of(yup.mixed<ImageTypeEnum>().required()) + .ensure() + .default([]), + date: partialDateSchema(maxImageDate()).default(null), }), ) .required(), diff --git a/frontend/src/pages/performers/performerForm/types.ts b/frontend/src/pages/performers/performerForm/types.ts index 2801199ca..c44c11355 100644 --- a/frontend/src/pages/performers/performerForm/types.ts +++ b/frontend/src/pages/performers/performerForm/types.ts @@ -1,3 +1,4 @@ +import type { TypedImage } from "src/components/editImages"; import type { BreastTypeEnum, EthnicityEnum, @@ -32,12 +33,7 @@ export type InitialPerformer = { hip_size?: number | null; band_size?: number | null; cup_size?: string | null; - images?: { - id: string; - url: string; - width: number; - height: number; - }[]; + images?: TypedImage[]; tattoos?: { location: string; description?: string | null; diff --git a/frontend/src/pages/scenes/SceneEditUpdate.tsx b/frontend/src/pages/scenes/SceneEditUpdate.tsx index 4cd2158ae..b11a141db 100644 --- a/frontend/src/pages/scenes/SceneEditUpdate.tsx +++ b/frontend/src/pages/scenes/SceneEditUpdate.tsx @@ -1,5 +1,6 @@ import { type FC, useState } from "react"; import { useNavigate } from "react-router-dom"; +import { toTypedImages } from "src/components/editImages"; import { type EditUpdateQuery, @@ -66,7 +67,10 @@ export const SceneEditUpdate: FC<{ edit: EditUpdate }> = ({ edit }) => { <hr /> <SceneForm scene={edit.target} - initial={edit.details} + initial={{ + ...edit.details, + images: toTypedImages(edit.details.images), + }} callback={doUpdate} saving={saving} /> diff --git a/frontend/src/pages/scenes/sceneForm/SceneForm.tsx b/frontend/src/pages/scenes/sceneForm/SceneForm.tsx index 110d841ee..205372af8 100644 --- a/frontend/src/pages/scenes/sceneForm/SceneForm.tsx +++ b/frontend/src/pages/scenes/sceneForm/SceneForm.tsx @@ -11,7 +11,10 @@ import { Menu, MenuItem, Typeahead } from "react-bootstrap-typeahead"; import { Controller, useFieldArray, useForm } from "react-hook-form"; import { Link } from "react-router-dom"; import { renderSceneDetails } from "src/components/editCard/ModifyEdit"; -import EditImages from "src/components/editImages"; +import EditImages, { + type TypedImage, + toTypedImages, +} from "src/components/editImages"; import { EditNote, NavButtons, SubmitButtons } from "src/components/form"; import { GenderIcon, Icon } from "src/components/fragments"; import SearchField, { @@ -24,7 +27,7 @@ import URLInput from "src/components/urlInput"; import { type FingerprintAlgorithm, type GenderEnum, - type ImageFragment, + ImageTypeScopeEnum, type SceneFragment as Scene, type SceneEditDetailsInput, ValidSiteTypeEnum, @@ -80,7 +83,7 @@ const SceneForm: FC<SceneProps> = ({ director: initial?.director ?? scene?.director, code: initial?.code ?? scene?.code, urls: initial?.urls ?? scene?.urls ?? [], - images: initial?.images ?? scene?.images ?? [], + images: initial?.images ?? toTypedImages(scene?.images ?? []), studio: initial?.studio ?? scene?.studio ?? undefined, tags: initial?.tags ?? scene?.tags ?? [], performers: (initial?.performers ?? scene?.performers ?? []).map((p) => ({ @@ -137,7 +140,7 @@ const SceneForm: FC<SceneProps> = ({ performer_id: performance.performerId, as: performance.alias, })), - image_ids: data.images.map((i) => i.id), + image_ids: data.images.map((i) => i.image.id), tag_ids: data.tags?.map((t) => t.id), urls: data.urls?.map((u) => ({ url: u.url, @@ -507,11 +510,12 @@ const SceneForm: FC<SceneProps> = ({ <Tab eventKey="images" title="Images"> <EditImages - lens={lens.focus("images").cast<ImageFragment[]>()} + lens={lens.focus("images").cast<TypedImage[]>()} maxImages={1} file={file} setFile={(f) => setFile(f)} - original={scene?.images} + original={toTypedImages(scene?.images ?? [])} + target={ImageTypeScopeEnum.SCENE} /> <NavButtons diff --git a/frontend/src/pages/scenes/sceneForm/__tests__/SceneForm.test.tsx b/frontend/src/pages/scenes/sceneForm/__tests__/SceneForm.test.tsx index 2ffdf4dc2..9bf673551 100644 --- a/frontend/src/pages/scenes/sceneForm/__tests__/SceneForm.test.tsx +++ b/frontend/src/pages/scenes/sceneForm/__tests__/SceneForm.test.tsx @@ -11,9 +11,15 @@ import { renderForm } from "src/test/renderForm"; import { describe, expect, it, vi } from "vitest"; import SceneForm from "../SceneForm"; -vi.mock("src/components/editImages", () => ({ - default: () => <div data-testid="edit-images" />, -})); +// Only the component is stubbed: toTypedImages is a pure helper the form +// needs for its initial values +vi.mock("src/components/editImages", async (orig) => { + const real = (await orig()) as typeof import("src/components/editImages"); + return { + ...real, + default: () => <div data-testid="edit-images" />, + }; +}); vi.mock("src/components/editCard/ModifyEdit", async (orig) => { const real = (await orig()) as typeof import("src/components/editCard/ModifyEdit"); diff --git a/frontend/src/pages/scenes/sceneForm/__tests__/diff.test.ts b/frontend/src/pages/scenes/sceneForm/__tests__/diff.test.ts index 3444e1bf7..de1747feb 100644 --- a/frontend/src/pages/scenes/sceneForm/__tests__/diff.test.ts +++ b/frontend/src/pages/scenes/sceneForm/__tests__/diff.test.ts @@ -16,6 +16,13 @@ const image = (id: string) => ({ height: 100, }); +// The form carries images wrapped with their labels so the diff unwraps them +const typedImage = (id: string) => ({ + image: image(id), + types: [], + date: null, +}); + const baseScene = (overrides: Partial<SceneFragment> = {}): SceneFragment => ({ id: "s-1", @@ -56,7 +63,7 @@ const baseForm = (overrides: Partial<SceneFormData> = {}): SceneFormData => code: "CODE", studio: { id: "stu-1", name: "Studio" }, urls: [{ url: "https://a", site: site("1") }], - images: [image("img-1")], + images: [typedImage("img-1")], performers: [ { performerId: "perf-1", @@ -211,7 +218,7 @@ describe("selectSceneDetails", () => { it("diffs image add/remove", () => { const [, neu] = selectSceneDetails( - baseForm({ images: [image("img-2")] }), + baseForm({ images: [typedImage("img-2")] }), baseScene(), ); expect(neu.added_images).toEqual([image("img-2")]); diff --git a/frontend/src/pages/scenes/sceneForm/__tests__/schema.test.ts b/frontend/src/pages/scenes/sceneForm/__tests__/schema.test.ts new file mode 100644 index 000000000..c47635daa --- /dev/null +++ b/frontend/src/pages/scenes/sceneForm/__tests__/schema.test.ts @@ -0,0 +1,53 @@ +import { PerformerSchema } from "src/pages/performers/performerForm/schema"; +import { describe, expect, it } from "vitest"; + +import { SceneSchema } from "../schema"; + +const CASES = [ + { date: "2019", valid: true }, + { date: "2019-06", valid: true }, + { date: "2019-06-15", valid: true }, + { date: "19", valid: false }, + { date: "2019-6", valid: false }, + { date: "06-2019", valid: false }, + { date: "2019-13", valid: false }, + { date: "2019-02-30", valid: false }, + { date: "1899", valid: false }, +]; + +const sceneAccepts = async (date: string) => { + try { + await SceneSchema.validateAt("date", { date }); + return true; + } catch { + return false; + } +}; + +const imageDateAccepts = async (date: string) => { + try { + await PerformerSchema.validateAt("images[0].date", { + images: [{ image: { id: "x" }, types: [], date: date }], + }); + return true; + } catch { + return false; + } +}; + +describe("partial date fields agree with each other", () => { + for (const { date, valid } of CASES) { + it(`${valid ? "accepts" : "rejects"} ${date}`, async () => { + expect(await sceneAccepts(date)).toBe(valid); + expect(await imageDateAccepts(date)).toBe(valid); + }); + } + + // Scenes can have dates in the future but images literally cannot exist + // before they are taken / created + it("differs only on future dates", async () => { + const nextYear = `${new Date().getFullYear() + 1}`; + expect(await sceneAccepts(nextYear)).toBe(true); + expect(await imageDateAccepts(nextYear)).toBe(false); + }); +}); diff --git a/frontend/src/pages/scenes/sceneForm/diff.ts b/frontend/src/pages/scenes/sceneForm/diff.ts index 1ce3ec18e..15156bf3d 100644 --- a/frontend/src/pages/scenes/sceneForm/diff.ts +++ b/frontend/src/pages/scenes/sceneForm/diff.ts @@ -73,7 +73,7 @@ const selectSceneDetails = ( ); const [addedImages, removedImages] = diffImages( - data.images, + data.images.map((i) => i.image), original?.images ?? [], ); const [addedUrls, removedUrls] = diffURLs(data.urls, original?.urls ?? []); diff --git a/frontend/src/pages/scenes/sceneForm/schema.ts b/frontend/src/pages/scenes/sceneForm/schema.ts index c849e9b45..c0ca5b3a8 100644 --- a/frontend/src/pages/scenes/sceneForm/schema.ts +++ b/frontend/src/pages/scenes/sceneForm/schema.ts @@ -1,5 +1,6 @@ +import type { ImageTypeEnum } from "src/graphql"; import { GenderEnum } from "src/graphql"; -import { isDateInRange, isValidDate, maxReleaseDate } from "src/utils"; +import { maxImageDate, maxReleaseDate, partialDateSchema } from "src/utils"; import * as yup from "yup"; const nullCheck = (input: string | null) => @@ -8,35 +9,10 @@ const nullCheck = (input: string | null) => export const SceneSchema = yup.object({ title: yup.string().trim().required("Title is required"), details: yup.string().trim(), - date: yup - .string() - .trim() + date: partialDateSchema(maxReleaseDate()) .defined() - .transform(nullCheck) - .matches(/^\d{4}$|^\d{4}-\d{2}$|^\d{4}-\d{2}-\d{2}$/, { - excludeEmptyString: true, - message: "Invalid date, must be YYYY, YYYY-MM, or YYYY-MM-DD", - }) - .test("valid-date", "Invalid date", isValidDate) - .test("date-outside-range", "Outside of range", (date) => - isDateInRange(date, maxReleaseDate()), - ) - .nullable() .required("Release date is required"), - production_date: yup - .string() - .trim() - .defined() - .transform(nullCheck) - .matches(/^\d{4}$|^\d{4}-\d{2}$|^\d{4}-\d{2}-\d{2}$/, { - excludeEmptyString: true, - message: "Invalid date, must be YYYY, YYYY-MM, or YYYY-MM-DD", - }) - .test("valid-date", "Invalid date", isValidDate) - .test("date-outside-range", "Outside of range", (date) => - isDateInRange(date, maxReleaseDate()), - ) - .nullable(), + production_date: partialDateSchema(maxReleaseDate()).defined(), duration: yup .string() .trim() @@ -99,10 +75,18 @@ export const SceneSchema = yup.object({ .array() .of( yup.object({ - id: yup.string().required(), - url: yup.string().required(), - width: yup.number().required(), - height: yup.number().required(), + image: yup.object({ + id: yup.string().required(), + url: yup.string().required(), + width: yup.number().required(), + height: yup.number().required(), + }), + types: yup + .array() + .of(yup.mixed<ImageTypeEnum>().required()) + .ensure() + .default([]), + date: partialDateSchema(maxImageDate()).default(null), }), ) .required(), diff --git a/frontend/src/pages/scenes/sceneForm/types.ts b/frontend/src/pages/scenes/sceneForm/types.ts index d268eebd7..95d3c9ebf 100644 --- a/frontend/src/pages/scenes/sceneForm/types.ts +++ b/frontend/src/pages/scenes/sceneForm/types.ts @@ -1,3 +1,4 @@ +import type { TypedImage } from "src/components/editImages"; import type { GenderEnum } from "src/graphql"; export type InitialScene = { @@ -15,12 +16,7 @@ export type InitialScene = { name: string; }; }[]; - images?: { - id: string; - width: number; - height: number; - url: string; - }[]; + images?: TypedImage[]; studio?: { id: string; name: string; diff --git a/frontend/src/pages/search/PerformerCard.tsx b/frontend/src/pages/search/PerformerCard.tsx index 3e4fde350..c6951e1e1 100644 --- a/frontend/src/pages/search/PerformerCard.tsx +++ b/frontend/src/pages/search/PerformerCard.tsx @@ -25,7 +25,7 @@ export const PerformerCard: FC<{ performer: Performer }> = ({ performer }) => ( <Card> <Thumbnail orientation="portrait" - image={performer.images[0]?.url} + image={performer.thumbnail?.url} className="SearchPage-performer-image" size={300} /> diff --git a/frontend/src/pages/studios/StudioEditUpdate.tsx b/frontend/src/pages/studios/StudioEditUpdate.tsx index 4d4c8f592..75bd6b5b0 100644 --- a/frontend/src/pages/studios/StudioEditUpdate.tsx +++ b/frontend/src/pages/studios/StudioEditUpdate.tsx @@ -1,5 +1,6 @@ import { type FC, useState } from "react"; import { useNavigate } from "react-router-dom"; +import { toTypedImages } from "src/components/editImages"; import { type EditUpdateQuery, @@ -63,7 +64,10 @@ export const StudioEditUpdate: FC<{ edit: EditUpdate }> = ({ edit }) => { <hr /> <StudioForm studio={edit.target} - initial={edit.details} + initial={{ + ...edit.details, + images: toTypedImages(edit.details.images), + }} callback={doUpdate} saving={saving} /> diff --git a/frontend/src/pages/studios/studioForm/StudioForm.tsx b/frontend/src/pages/studios/studioForm/StudioForm.tsx index 7139d59f4..bf2c8ee29 100644 --- a/frontend/src/pages/studios/studioForm/StudioForm.tsx +++ b/frontend/src/pages/studios/studioForm/StudioForm.tsx @@ -7,14 +7,17 @@ import { Col, Form, Row, Tab, Tabs } from "react-bootstrap"; import { Controller, useForm } from "react-hook-form"; import { Link } from "react-router-dom"; import { renderStudioDetails } from "src/components/editCard/ModifyEdit"; -import EditImages from "src/components/editImages"; +import EditImages, { + type TypedImage, + toTypedImages, +} from "src/components/editImages"; import { EditNote, NavButtons, SubmitButtons } from "src/components/form"; import { Icon } from "src/components/fragments"; import MultiSelect from "src/components/multiSelect"; import StudioSelect from "src/components/studioSelect"; import URLInput from "src/components/urlInput"; import { - type ImageFragment, + ImageTypeScopeEnum, type StudioFragment as Studio, type StudioEditDetailsInput, ValidSiteTypeEnum, @@ -52,7 +55,7 @@ const StudioForm: FC<StudioProps> = ({ defaultValues: { name: initial?.name ?? studio?.name, aliases: initialAliases, - images: initial?.images ?? studio?.images ?? [], + images: initial?.images ?? toTypedImages(studio?.images ?? []), urls: initial?.urls ?? studio?.urls ?? [], parent: initial?.parent ?? studio?.parent, }, @@ -83,7 +86,7 @@ const StudioForm: FC<StudioProps> = ({ url: u.url, site_id: u.site.id, })), - image_ids: data.images.map((i) => i.id), + image_ids: data.images.map((i) => i.image.id), parent_id: data.parent?.id ?? null, }; callback(callbackData, data.note); @@ -174,11 +177,12 @@ const StudioForm: FC<StudioProps> = ({ <Tab eventKey="images" title="Images" className="col-xl-6"> <EditImages - lens={lens.focus("images").cast<ImageFragment[]>()} + lens={lens.focus("images").cast<TypedImage[]>()} maxImages={1} file={file} setFile={(f) => setFile(f)} allowLossless + target={ImageTypeScopeEnum.STUDIO} /> <NavButtons diff --git a/frontend/src/pages/studios/studioForm/__tests__/StudioForm.test.tsx b/frontend/src/pages/studios/studioForm/__tests__/StudioForm.test.tsx index 3f425a5ec..6c70a9a6c 100644 --- a/frontend/src/pages/studios/studioForm/__tests__/StudioForm.test.tsx +++ b/frontend/src/pages/studios/studioForm/__tests__/StudioForm.test.tsx @@ -6,9 +6,15 @@ import { addCreatableOption, removeMultiValue } from "src/test/selectors"; import { describe, expect, it, vi } from "vitest"; import StudioForm from "../StudioForm"; -vi.mock("src/components/editImages", () => ({ - default: () => <div data-testid="edit-images" />, -})); +// Only the component is stubbed: toTypedImages is a pure helper the form +// needs for its initial values +vi.mock("src/components/editImages", async (orig) => { + const real = (await orig()) as typeof import("src/components/editImages"); + return { + ...real, + default: () => <div data-testid="edit-images" />, + }; +}); vi.mock("src/components/editCard/ModifyEdit", async (orig) => { const real = (await orig()) as typeof import("src/components/editCard/ModifyEdit"); diff --git a/frontend/src/pages/studios/studioForm/__tests__/diff.test.ts b/frontend/src/pages/studios/studioForm/__tests__/diff.test.ts index 8c7671da8..ec8b29ecf 100644 --- a/frontend/src/pages/studios/studioForm/__tests__/diff.test.ts +++ b/frontend/src/pages/studios/studioForm/__tests__/diff.test.ts @@ -16,6 +16,13 @@ const image = (id: string) => ({ height: 100, }); +// The form carries images wrapped with their labels so the diff unwraps them +const typedImage = (id: string) => ({ + image: image(id), + types: [], + date: null, +}); + const baseStudio = (overrides: Partial<StudioFragment> = {}): StudioFragment => ({ id: "studio-1", @@ -32,7 +39,7 @@ const baseForm = (overrides: Partial<StudioFormData> = {}): StudioFormData => name: "Studio One", aliases: ["alt-1"], urls: [{ url: "https://a", site: site("1") }], - images: [image("img-1")], + images: [typedImage("img-1")], parent: { id: "parent-1", name: "Parent" }, note: "n", ...overrides, @@ -110,7 +117,7 @@ describe("selectStudioDetails", () => { it("diffs image add/remove", () => { const [, neu] = selectStudioDetails( - baseForm({ images: [image("img-2")] }), + baseForm({ images: [typedImage("img-2")] }), baseStudio(), ); expect(neu.added_images).toEqual([image("img-2")]); diff --git a/frontend/src/pages/studios/studioForm/diff.ts b/frontend/src/pages/studios/studioForm/diff.ts index ee8f9b848..64609db50 100644 --- a/frontend/src/pages/studios/studioForm/diff.ts +++ b/frontend/src/pages/studios/studioForm/diff.ts @@ -11,7 +11,7 @@ const selectStudioDetails = ( original: StudioFragment | null | undefined, ): [Required<OldStudioDetails>, Required<StudioDetails>] => { const [addedImages, removedImages] = diffImages( - data.images, + data.images.map((i) => i.image), original?.images ?? [], ); const [addedUrls, removedUrls] = diffURLs(data.urls, original?.urls ?? []); diff --git a/frontend/src/pages/studios/studioForm/schema.ts b/frontend/src/pages/studios/studioForm/schema.ts index 808f46f74..2153b7749 100644 --- a/frontend/src/pages/studios/studioForm/schema.ts +++ b/frontend/src/pages/studios/studioForm/schema.ts @@ -1,3 +1,5 @@ +import type { ImageTypeEnum } from "src/graphql"; +import { maxImageDate, partialDateSchema } from "src/utils"; import * as yup from "yup"; export const StudioSchema = yup.object({ @@ -22,10 +24,18 @@ export const StudioSchema = yup.object({ .array() .of( yup.object({ - id: yup.string().required(), - url: yup.string().required(), - width: yup.number().required(), - height: yup.number().required(), + image: yup.object({ + id: yup.string().required(), + url: yup.string().required(), + width: yup.number().required(), + height: yup.number().required(), + }), + types: yup + .array() + .of(yup.mixed<ImageTypeEnum>().required()) + .ensure() + .default([]), + date: partialDateSchema(maxImageDate()).default(null), }), ) .required(), diff --git a/frontend/src/pages/studios/studioForm/types.ts b/frontend/src/pages/studios/studioForm/types.ts index 156676148..0c3435a35 100644 --- a/frontend/src/pages/studios/studioForm/types.ts +++ b/frontend/src/pages/studios/studioForm/types.ts @@ -1,3 +1,5 @@ +import type { TypedImage } from "src/components/editImages"; + export type InitialStudio = { name?: string | null; aliases?: string[]; @@ -5,12 +7,7 @@ export type InitialStudio = { id: string; name: string; } | null; - images?: { - id: string; - height: number; - width: number; - url: string; - }[]; + images?: TypedImage[]; urls?: { url: string; site: { diff --git a/frontend/src/pages/users/User.tsx b/frontend/src/pages/users/User.tsx index c083e81c8..9f7636360 100644 --- a/frontend/src/pages/users/User.tsx +++ b/frontend/src/pages/users/User.tsx @@ -15,6 +15,7 @@ import { EditStatusTypes, VoteTypes } from "src/constants"; import { ROUTE_USER_EDIT, ROUTE_USER_EDITS, + ROUTE_USER_IMAGE_PREFERENCES, ROUTE_USER_MY_FINGERPRINTS, ROUTE_USER_PASSWORD, ROUTE_USERS, @@ -312,6 +313,12 @@ const UserComponent: FC<Props> = ({ user, refetch }) => { <Link to={ROUTE_USER_MY_FINGERPRINTS} className="ms-2"> <Button variant="secondary">My Fingerprints</Button> </Link> + <Link + to={createHref(ROUTE_USER_IMAGE_PREFERENCES, user)} + className="ms-2" + > + <Button variant="secondary">Image Preferences</Button> + </Link> <Link to={ROUTE_USER_PASSWORD} className="ms-2"> <Button>Change Password</Button> </Link> diff --git a/frontend/src/pages/users/UserImageTypePreferences.tsx b/frontend/src/pages/users/UserImageTypePreferences.tsx new file mode 100644 index 000000000..a193b8154 --- /dev/null +++ b/frontend/src/pages/users/UserImageTypePreferences.tsx @@ -0,0 +1,177 @@ +import { type FC, useEffect, useState } from "react"; +import { Button, Card } from "react-bootstrap"; + +import { DragList } from "src/components/dragList"; +import { ErrorMessage, LoadingIndicator } from "src/components/fragments"; +import { + type ImageTypeEnum, + type ImageTypeGroupEnum, + useImageTypeGroups, + useUpdateImageTypePreferences, +} from "src/graphql"; + +interface Props { + user: { + id: string; + image_type_preferences: ImageTypeEnum[]; + image_type_group_preferences: ImageTypeGroupEnum[]; + }; +} + +type PreferenceGroup = { + key: ImageTypeGroupEnum; + name: string; + description?: string | null; + types: { key: ImageTypeEnum; name: string }[]; +}; + +/** + * Sorts by a user's stated order, with anything they did not mention trailing + * in the order it arrived. That is exactly how the server resolves a partial + * preference, so what is shown is what is applied. + */ +const byPreference = <T, K>(items: T[], keyOf: (item: T) => K, order: K[]) => { + const rank = new Map(order.map((key, index) => [key, index])); + return [...items].sort((a, b) => { + const rankA = rank.get(keyOf(a)); + const rankB = rank.get(keyOf(b)); + if (rankA !== undefined && rankB !== undefined) return rankA - rankB; + if (rankA !== undefined) return -1; + if (rankB !== undefined) return 1; + return 0; + }); +}; + +/** + * Orders the image type vocabulary for this user alone: both which dimension + * is compared first and which values lead within it + * + * Group order is the stronger of the two: it decides which dimension wins, + * where type order only breaks ties inside one, so a preference confined to + * types could not express itself at all when the dimension someone cared about + * was compared last + */ +export const UserImageTypePreferences: FC<Props> = ({ user }) => { + const { loading, data } = useImageTypeGroups({}); + const [updatePreferences, { loading: saving }] = + useUpdateImageTypePreferences(); + + const [groups, setGroups] = useState<PreferenceGroup[]>([]); + const [saved, setSaved] = useState(false); + const [error, setError] = useState<string>(); + + useEffect(() => { + if (!data?.imageTypeGroups) return; + + setGroups( + byPreference( + data.imageTypeGroups.map((group) => ({ + ...group, + types: byPreference( + group.types, + (type) => type.key, + user.image_type_preferences, + ), + })), + (group) => group.key, + user.image_type_group_preferences, + ), + ); + }, [data, user.image_type_preferences, user.image_type_group_preferences]); + + if (loading) return <LoadingIndicator message="Loading image types..." />; + + const reorderGroup = ( + groupIndex: number, + types: PreferenceGroup["types"], + ) => { + setSaved(false); + setGroups((current) => + current.map((group, i) => + i === groupIndex ? { ...group, types } : group, + ), + ); + }; + + const submit = (input: { + types: ImageTypeEnum[]; + groups: ImageTypeGroupEnum[]; + }) => { + setError(undefined); + updatePreferences({ variables: { input } }) + .then(() => setSaved(true)) + .catch((e: unknown) => + setError(e instanceof Error ? e.message : String(e)), + ); + }; + + const save = () => + submit({ + types: groups.flatMap((group) => group.types.map((type) => type.key)), + groups: groups.map((group) => group.key), + }); + + const clear = () => submit({ types: [], groups: [] }); + + return ( + <> + <h4>Image preferences</h4> + <hr /> + <p className="text-muted"> + Sets which of an entity's images you see first, including through + the API. Whatever you put at the top wins: the dimensions are compared + from the top down, and within each one its values are. Drag by the + handles to reorder, or focus a handle and use the arrow keys. + </p> + + {error && <ErrorMessage error={error} />} + {saved && <div className="text-success mb-2">Preferences saved.</div>} + + <DragList + className="is-block" + items={groups} + keyOf={(group) => group.key} + labelOf={(group) => `the ${group.name} group`} + onReorder={(next) => { + setSaved(false); + setGroups(next); + }} + > + {(group) => ( + <Card className="mb-3"> + <Card.Header> + <b>{group.name}</b> + {group.description && ( + <small className="text-muted ms-2">{group.description}</small> + )} + </Card.Header> + <Card.Body> + <DragList + items={group.types} + keyOf={(type) => type.key} + labelOf={(type) => type.name} + onReorder={(types) => + reorderGroup( + groups.findIndex((g) => g.key === group.key), + types, + ) + } + > + {(type) => <span>{type.name}</span>} + </DragList> + </Card.Body> + </Card> + )} + </DragList> + + <div className="mt-4"> + <Button variant="secondary" onClick={clear} className="me-2"> + Use site default + </Button> + <Button onClick={save} disabled={saving}> + {saving ? "Saving..." : "Save"} + </Button> + </div> + </> + ); +}; diff --git a/frontend/src/pages/users/index.tsx b/frontend/src/pages/users/index.tsx index d4df593d9..aabe6f130 100644 --- a/frontend/src/pages/users/index.tsx +++ b/frontend/src/pages/users/index.tsx @@ -9,6 +9,7 @@ import UserConfirmChangeEmail from "./UserConfirmChangeEmail"; import UserEdit from "./UserEdit"; import UserEdits from "./UserEdits"; import UserFingerprints from "./UserFingerprints"; +import { UserImageTypePreferences } from "./UserImageTypePreferences"; import { UserNotificationPreferences } from "./UserNotificationPreferences"; import UserPassword from "./UserPassword"; import Users from "./Users"; @@ -62,6 +63,19 @@ const UserLoader: FC = () => { path="/change-email" element={<UserValidateChangeEmail user={user} />} /> + <Route + path="/image-types" + element={ + "image_type_preferences" in user ? ( + <> + <Title page={"Image Preferences"} /> + <UserImageTypePreferences user={user} /> + </> + ) : ( + <ErrorMessage error="Forbidden" /> + ) + } + /> <Route path="/notifications" element={ diff --git a/frontend/src/test/setup.ts b/frontend/src/test/setup.ts index c23266ff3..651bf3c98 100644 --- a/frontend/src/test/setup.ts +++ b/frontend/src/test/setup.ts @@ -53,3 +53,41 @@ if (typeof window !== "undefined") { Element.prototype.scrollIntoView = () => {}; } } + +// jsdom has neither of these, and the crop step needs both: an object URL to +// show the picture, and a decode to learn its size. Stubbed here rather than +// per test, since anything rendering a file preview wants them. +if (typeof window !== "undefined") { + if (!window.URL.createObjectURL) { + window.URL.createObjectURL = () => "blob:stub"; + window.URL.revokeObjectURL = () => {}; + } + + if (!window.createImageBitmap) { + // 200x300, so a test can tell a portrait frame from a landscape one. + window.createImageBitmap = (() => + Promise.resolve({ + width: 200, + height: 300, + close: () => {}, + })) as unknown as typeof createImageBitmap; + } +} + +// jsdom implements neither, and anything dragged with a pointer needs both: +// capture so the drag survives leaving the element, and a box to measure the +// drag against. +if (typeof window !== "undefined") { + for (const method of [ + "setPointerCapture", + "releasePointerCapture", + "hasPointerCapture", + ] as const) { + if (!Element.prototype[method]) { + Object.defineProperty(Element.prototype, method, { + value: () => false, + writable: true, + }); + } + } +} diff --git a/frontend/src/utils/__tests__/date.test.ts b/frontend/src/utils/__tests__/date.test.ts index 86c05136b..44e597daf 100644 --- a/frontend/src/utils/__tests__/date.test.ts +++ b/frontend/src/utils/__tests__/date.test.ts @@ -7,9 +7,11 @@ import { isValidDate, maxBirthdate, maxDeathdate, + maxImageDate, maxReleaseDate, parseDate, parseInstant, + partialDateError, } from "../date"; describe("isValidDate", () => { @@ -146,3 +148,39 @@ describe("formatISODate", () => { expect(formatISODate(new Date("2024-05-17T00:00:00Z"))).toBe("2024-05-17"); }); }); + +describe("partialDateError", () => { + const end = maxImageDate(); + + it("accepts the three precisions, and nothing", () => { + for (const date of ["2019", "2019-06", "2019-06-15", "", null, undefined]) { + expect(partialDateError(date, end)).toBeUndefined(); + } + }); + + it("names the shape when the shape is wrong", () => { + for (const date of [ + "19", + "2019-6", + "06-2019", + "yesterday", + "2019-06-15T00:00", + ]) { + expect(partialDateError(date, end)).toBe( + "Invalid date, must be YYYY, YYYY-MM, or YYYY-MM-DD", + ); + } + }); + + it("rejects a date outside the range", () => { + expect(partialDateError("1899", end)).toBe("Outside of range"); + expect(partialDateError(`${end.year + 1}`, end)).toBe("Outside of range"); + }); + + // TODO: only the frontend rejects these because of Temporal + // the backend accepts any string at all + it("rejects a day that does not exist", () => { + expect(partialDateError("2019-02-30", end)).toBe("Invalid date"); + expect(partialDateError("2019-13", end)).toBe("Invalid date"); + }); +}); diff --git a/frontend/src/utils/date.ts b/frontend/src/utils/date.ts index 807e66c50..7b73bb3d9 100644 --- a/frontend/src/utils/date.ts +++ b/frontend/src/utils/date.ts @@ -35,6 +35,8 @@ export const maxBirthdate = () => export const maxDeathdate = () => Temporal.Now.plainDateISO(); export const maxReleaseDate = () => Temporal.Now.plainDateISO().add({ years: 1 }); +// Today, unlike a release date: a scene can be announced before it exists but an image cannot be from the future +export const maxImageDate = () => Temporal.Now.plainDateISO(); export const isInstantInFuture = (instant: Temporal.Instant) => Temporal.Instant.compare(instant, Temporal.Now.instant()) > 0; @@ -42,6 +44,9 @@ export const isInstantInFuture = (instant: Temporal.Instant) => export const formatInstant = (instant: Temporal.Instant) => instant.toZonedDateTimeISO(Temporal.Now.timeZoneId()).toLocaleString(); +/** The three precisions a partial date may be written to. */ +export const PARTIAL_DATE = /^\d{4}$|^\d{4}-\d{2}$|^\d{4}-\d{2}-\d{2}$/; + const expandPartialDate = (date: string) => { if (/^\d{4}$/.test(date)) return `${date}-01-01`; if (/^\d{4}-\d{2}$/.test(date)) return `${date}-01`; @@ -78,6 +83,18 @@ export const isDateInRange = ( return true; }; +export const partialDateError = ( + date: string | null | undefined, + end: Temporal.PlainDate, +): string | undefined => { + if (!date) return undefined; + if (!PARTIAL_DATE.test(date)) + return "Invalid date, must be YYYY, YYYY-MM, or YYYY-MM-DD"; + if (!isValidDate(date)) return "Invalid date"; + if (!isDateInRange(date, end)) return "Outside of range"; + return undefined; +}; + export const formatDistance = ( from: Temporal.Instant, to?: Temporal.Instant, diff --git a/frontend/src/utils/dateSchema.ts b/frontend/src/utils/dateSchema.ts new file mode 100644 index 000000000..055e7ea63 --- /dev/null +++ b/frontend/src/utils/dateSchema.ts @@ -0,0 +1,21 @@ +import type { Temporal } from "temporal-polyfill"; +import * as yup from "yup"; + +import { isDateInRange, isValidDate, PARTIAL_DATE } from "./date"; + +export const partialDateSchema = (end: Temporal.PlainDate) => + yup + .string() + .trim() + .transform((input: string | null) => + input === "" || input === "null" ? null : input, + ) + .matches(PARTIAL_DATE, { + excludeEmptyString: true, + message: "Invalid date, must be YYYY, YYYY-MM, or YYYY-MM-DD", + }) + .test("valid-date", "Invalid date", isValidDate) + .test("date-outside-range", "Outside of range", (date) => + isDateInRange(date, end), + ) + .nullable(); diff --git a/frontend/src/utils/diff.ts b/frontend/src/utils/diff.ts index 744db66bd..a113791c1 100644 --- a/frontend/src/utils/diff.ts +++ b/frontend/src/utils/diff.ts @@ -77,3 +77,42 @@ export const diffURLs = ( })), (u) => `${u.site.name ?? "Unknown"}: ${u.url}`, ); + +export const diffImageLabels = <TImage extends { id: string }>( + newImages: { + image: TImage; + types: string[]; + date?: string | null; + }[], + oldImages: { + image: { id: string }; + types: string[]; + date?: string | null; + }[], +) => { + const previous = new Map(oldImages.map((entry) => [entry.image.id, entry])); + + return newImages.flatMap((entry) => { + const before = previous.get(entry.image.id); + const beforeTypes = before?.types ?? []; + + const added = entry.types.filter((type) => !beforeTypes.includes(type)); + const removed = beforeTypes.filter((type) => !entry.types.includes(type)); + + const beforeDate = before?.date ?? null; + const afterDate = entry.date || null; + const dateChanged = before !== undefined && beforeDate !== afterDate; + + if (added.length === 0 && removed.length === 0 && !dateChanged) return []; + + return [ + { + image: entry.image, + added_types: added, + removed_types: removed, + date: afterDate, + date_changed: dateChanged, + }, + ]; + }); +}; diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts index 8c81b3e05..a267d9175 100644 --- a/frontend/src/utils/index.ts +++ b/frontend/src/utils/index.ts @@ -1,6 +1,7 @@ export * from "./country"; export * from "./data"; export * from "./date"; +export * from "./dateSchema"; export * from "./diff"; export * from "./edit"; export * from "./enum"; diff --git a/gqlgen.yml b/gqlgen.yml index 46313e6bf..f56aa8a9f 100644 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -23,6 +23,12 @@ models: fields: url: resolver: true + ImageType: + # Resolved from the template loader rather than carried on the model, so a + # file is read only when a client asks for the frame + fields: + crop_template: + resolver: true URLInput: model: github.com/stashapp/stash-box/internal/models.URL QueryPerformersResultType: diff --git a/graphql/schema/schema.graphql b/graphql/schema/schema.graphql index ad30fac82..90a312542 100644 --- a/graphql/schema/schema.graphql +++ b/graphql/schema/schema.graphql @@ -74,6 +74,19 @@ type Query { """Discover favicon candidates for a URL, returned as base64 data URLs""" fetchSiteFavicons(url: String!): [SiteFavicon!]! @hasRole(role: ADMIN) + #### Image types #### + + """ + The image type vocabulary, groups in priority order with their types nested. + Filtering by target drops types that entity kind cannot carry, and drops any + group thereby left empty. + + Disabled groups and types are omitted unless asked for: a labeller should not + see what the instance has switched off, but the admin who switched it off has + to be able to switch it back on. + """ + imageTypeGroups(target: ImageTypeScopeEnum, include_disabled: Boolean = false): [ImageTypeGroup!]! @hasRole(role: READ) + #### Edits #### findEdit(id: ID!): Edit @hasRole(role: READ) @@ -174,6 +187,20 @@ type Mutation { siteCategoryUpdate(input: SiteCategoryUpdateInput!): SiteCategory @hasRole(role: ADMIN) siteCategoryDestroy(input: SiteCategoryDestroyInput!): Boolean! @hasRole(role: ADMIN) + """ + Reorder the image type vocabulary, deciding which image ranks first + instance-wide. Both lists must be complete; returns the reordered vocabulary. + """ + imageTypeOrderUpdate(input: ImageTypeOrderInput!): [ImageTypeGroup!]! @hasRole(role: ADMIN) + + """ + Choose which of the vocabulary this instance uses. Takes the complete set of + keys to switch off, so anything absent is on; returns the whole vocabulary, + disabled entries included. Nothing is deleted, so switching a group back on + restores every label made while it was in use. + """ + imageTypeSetEnabled(input: ImageTypeEnabledInput!): [ImageTypeGroup!]! @hasRole(role: ADMIN) + """Regenerates the api key for the given user, or the current user if id not provided""" regenerateAPIKey(userID: ID): String! @@ -248,6 +275,13 @@ type Mutation { markNotificationsRead(notification: MarkNotificationReadInput): Boolean! @hasRole(role: READ) """Update notification subscriptions for current user.""" updateNotificationSubscriptions(subscriptions: [NotificationEnum!]!): Boolean! @hasRole(role: READ) + + """ + Reorder image types for the current user, and optionally the groups they sit + in. Unlike the admin ordering both lists may be partial: anything left out + trails what was listed, in instance order. Empty lists clear that preference. + """ + updateImageTypePreferences(input: ImageTypePreferencesInput!): Boolean! @hasRole(role: READ) } schema { diff --git a/graphql/schema/types/image.graphql b/graphql/schema/types/image.graphql index 5e1b32a0b..85b18eb39 100644 --- a/graphql/schema/types/image.graphql +++ b/graphql/schema/types/image.graphql @@ -10,6 +10,38 @@ type Image { input ImageCreateInput { url: String file: Upload + crop: ImageCropInput +} + +""" +A frame to cut an upload down to, in the coordinates the client is looking at. + +Cropping happens here rather than in the browser for two reasons. A canvas +re-encode is a second lossy generation on top of whatever the contributor +started with, where the server decodes once and encodes once. And images are +deduplicated on a checksum of their stored bytes, which stops working if the +bytes are produced by whichever encoder the uploader's browser happens to +have: two people cropping the same source to the same frame would land as two +images +""" +input ImageCropInput { + """Distance from the left edge, as a fraction of the width""" + x: Float! + """Distance from the top edge, as a fraction of the height""" + y: Float! + """Fraction of the width to keep""" + width: Float! + """Fraction of the height to keep""" + height: Float! + """ + Degrees to rotate clockwise before cutting, for a tilted horizon. The frame + above is measured against the rotated image, which is larger than the + original - the same thing the client is dragging over. + + EXIF orientation is applied before any of this, so the coordinates are the + ones a browser shows rather than the ones stored in the file + """ + angle: Float = 0 } input ImageUpdateInput { diff --git a/graphql/schema/types/image_type.graphql b/graphql/schema/types/image_type.graphql new file mode 100644 index 000000000..8e1302656 --- /dev/null +++ b/graphql/schema/types/image_type.graphql @@ -0,0 +1,341 @@ +"""A dimension of the image type vocabulary. Types within one group are ranked against each other.""" +enum ImageTypeGroupEnum { + SHOT + CROP + VIEW + POSTURE + DRESS +} + +""" +A label that may be applied to an image's presence on an entity. + +Every key is its group key followed by an underscore, so SHOT_PORTRAIT belongs +to the SHOT group. The vocabulary is fixed and identical on every instance, +which is what lets a client code against these values directly. +""" +enum ImageTypeEnum { + SHOT_PORTRAIT + SHOT_CANDID + SHOT_DETAIL + + CROP_FACE + CROP_BUST + CROP_THREE_QUARTER + CROP_THREE_QUARTER_PLUS + CROP_FULL_BODY + CROP_TORSO + CROP_WIDE + + VIEW_FRONT + VIEW_SIDE + VIEW_BACK + + POSTURE_STANDING + POSTURE_SITTING + POSTURE_KNEELING + POSTURE_SQUATTING + POSTURE_ON_ALL_FOURS + POSTURE_LYING + POSTURE_SUSPENDED + + DRESS_NON_NUDE + DRESS_UNDERWEAR + DRESS_TOPLESS + DRESS_NUDE + DRESS_EXPLICIT +} + +""" +The kinds of entity an image type may be applied to. + +Every value seeded today is PERFORMER-only. When scenes and studios get +image labelling, they get their own separate types and groups, not rows +here with SCENE or STUDIO added to a type's `valid_types` +""" +enum ImageTypeScopeEnum { + PERFORMER + SCENE + STUDIO +} + +type ImageTypeGroup { + key: ImageTypeGroupEnum! + name: String! + description: String + """Dimension priority when ranking images; lower wins""" + sort_order: Int! + """At most one type from this group may be assigned to an image""" + exclusive: Boolean! + """ + Whether this instance uses this dimension. A disabled group is not offered + when labelling and takes no part in ranking; existing assignments are kept, + so re-enabling restores them. + """ + enabled: Boolean! + types: [ImageType!]! +} + +""" +An image together with what it has been labelled on this entity. Not +performer-specific: scenes and studios expose the same type. +""" +type TypedImage { + image: Image! + types: [ImageTypeEnum!]! + """When the image is from. Partial ISO 8601: 2019, 2019-06, or 2019-06-15.""" + date: String +} + +""" +What an edit changes about one image's labels and date, grouped by image +rather than listed as flat added/removed tuples: one performer edit can +relabel a whole gallery. +""" +type ImageAssignmentChange { + image: Image! + added_types: [ImageTypeEnum!]! + removed_types: [ImageTypeEnum!]! + """ + The date this edit sets. Only meaningful when date_changed is true, where a + null means the edit clears the date. + """ + date: String + """Whether this edit changes the image's date at all.""" + date_changed: Boolean! +} + +""" +Everything said about one image's presence on an entity. An entry whose types +are empty clears that image's labels. + +**What each way of sending `image_types` means.** Three write paths implement +this: `performerCreate` and `performerUpdate` in Go, and the edit path in Go +at submission and SQL at apply. Nothing makes them agree but this table. + +| `image_types` is | performerCreate | performerUpdate | edit | +|---|---|---|---| +| absent | unlabelled | preserves all | preserves all | +| explicit `null` | unlabelled | **preserves all** | **clears all** | +| `[]` | unlabelled | clears all | clears all | +| non-empty | labels the images named | authoritative only over the images named | authoritative only over the images named | + +Null differs because the edit path is told which fields the client stated and +`performerUpdate` is not. **Send `[]` to clear on any path** and the question +does not arise. + +Note that a non-empty list leaves an image it does not mention exactly as it +was; otherwise every client touching `image_ids` would have to restate the +whole gallery's labels or destroy them. + +**And the same for `date`,** which is single-valued and so overrides +rather than merges: + +| the submission | the image's date | +|---|---| +| no entry for this image | kept | +| an entry stating `date` | set | +| an entry omitting `date` | cleared, see the field's own note | +| an entry omitting it, on an image being added | stays empty, and is not reported as a change | +""" +input ImageAssignmentInput { + image_id: ID! + types: [ImageTypeEnum!]! + """ + When the image is from. Partial ISO 8601: 2019, 2019-06, or 2019-06-15. + + An entry states the whole of what is true about its image, so omitting this + clears the date rather than leaving it. Send the current value back if the + change is only to the labels. + """ + date: String +} + +""" +A complete reordering of the vocabulary. Partial lists are rejected rather than +merged. +""" +input ImageTypeOrderInput { + """Groups in priority order. Must list every group exactly once.""" + groups: [ImageTypeGroupEnum!]! + """ + Types in priority order. Must list every type exactly once. Only position + within each group counts, so types of different groups may interleave freely. + """ + types: [ImageTypeEnum!]! +} + +""" +One user's ranking. Unlike the admin ordering both lists may be partial: a user +says what they care about and everything else keeps the instance order behind +it, which is what lets someone express "nudes first" without having to rank all +seventeen types. +""" +input ImageTypePreferencesInput { + """Types in preferred order, position within each group being what counts.""" + types: [ImageTypeEnum!]! + """ + Groups in preferred order, deciding which dimension is compared first. + + Absent leaves the group preference as it is; an empty list clears it. Not + defaulted, so a client sending only `types` keeps the group ordering it did + not mention. + """ + groups: [ImageTypeGroupEnum!] +} + +type ImageType { + key: ImageTypeEnum! + name: String! + description: String + """Value priority within the group; lower wins""" + sort_order: Int! + valid_types: [ImageTypeScopeEnum!]! + """Whether this instance uses this type. Disabled types cannot be assigned.""" + enabled: Boolean! + """ + Types this one cannot share an image with, across groups: a face crop cannot + be topless, because the chest is not in frame. Symmetric: each side + of a pair lists the other. Assigning both is rejected; a client should stop + offering the second once the first is chosen. + """ + conflicts_with: [ImageTypeEnum!]! + """ + The frame to crop to for this type, or null if the instance has no template + for it. Only crops have one - nothing about a pose or a state of dress says + anything about the shape of the picture + """ + crop_template: CropTemplate +} + +""" +A crop frame, read from a Photoshop template + +The template file is the source of truth: the guides drawn over the cropping +tool and the .psd a contributor can download for their own editor are the same +bytes, so the two cannot drift +""" +type CropTemplate { + """ + Width over height, taken from the template's canvas rather than set + anywhere + """ + aspect_ratio: Float! + guides: [CropGuide!]! + """ + Outlines drawn on the template's own layers like an oval for a face to sit + inside, a bar marking a margin. Guidance only: the crop is still a + rectangle, and nothing here changes what the server cuts + """ + shapes: [CropShape!]! +} + +"""One outline drawn in a crop template""" +type CropShape { + """ + What the template's author called the layer, like "head guide", "eyes soft + anchor", or null for an unnamed layer + """ + label: String + subpaths: [CropSubpath!]! +} + +""" +One continuous run of a shape's outline + +A shape can be several: a ring is an outer subpath and an inner one, and +whether each closes back on itself is the difference between an outline and an +arc +""" +type CropSubpath { + closed: Boolean! + knots: [CropKnot!]! +} + +""" +One anchor of an outline, with the control point either side of it + +Every segment is a cubic curve, including straight ones. Photoshop draws a +straight edge as a curve whose controls sit on its anchors, so a rectangle and +an ellipse arrive in the same shape +""" +type CropKnot { + """The control point governing the curve arriving at this anchor""" + control_in: CropPoint! + anchor: CropPoint! + """The control point governing the curve leaving it""" + control_out: CropPoint! +} + +""" +A position on the template's canvas, as fractions of its width and height + +Fractions like a guide's position, and for the same reason: a template is drawn +at one size and rendered at every other. Values outside 0 to 1 are legitimate: +a crop box is often drawn a hair outside the canvas so its stroke does not eat +into the picture +""" +type CropPoint { + x: Float! + y: Float! +} + +"""One guide line of a crop template""" +type CropGuide { + axis: CropGuideAxisEnum! + """ + Where the line sits, as a fraction of the canvas along its axis: 0 is the + left or top edge, 1 the right or bottom. A fraction rather than a pixel + because a template is drawn at one size and rendered at every other + """ + position: Float! + """ + How closely the line is meant to be followed, where the template says. An + anchor is meant to be hit; a reference is for judgement and balance + """ + role: CropGuideRoleEnum + """ + What the line is for, like "bisects the eyes", "where the thighs meet", or + null when the template does not name it + """ + label: String + """ + Whether a frame is resized around this line when the contributor holds + Shift + + Independent of `role`, which says how closely a line is meant to be + followed. A headshot's eye line is the softest line in its template (like the + head and chin can be hard limits) and is still the right thing to turn a + resize about, so the two cannot be the same field + + At most one guide per axis carries it. A template naming none on an axis + resizes about the centre there + """ + pivot: Boolean! +} + +enum CropGuideAxisEnum { + """A vertical line, positioned across the width""" + X + """A horizontal line, positioned down the height""" + Y +} + +enum CropGuideRoleEnum { + ANCHOR + REFERENCE + MARGIN +} + +""" +Which parts of the vocabulary an instance switches off. + +Expressed as what is disabled rather than what is enabled, so a type added to +the taxonomy later arrives switched on. +""" +input ImageTypeEnabledInput { + """Groups to switch off. A group being off implies its types are too.""" + disabled_groups: [ImageTypeGroupEnum!]! = [] + """Types to switch off individually, whatever their group's state.""" + disabled_types: [ImageTypeEnum!]! = [] +} diff --git a/graphql/schema/types/performer.graphql b/graphql/schema/types/performer.graphql index 10668f3e0..4791ffea1 100644 --- a/graphql/schema/types/performer.graphql +++ b/graphql/schema/types/performer.graphql @@ -111,7 +111,23 @@ type Performer { career_end_year: Int tattoos: [BodyModification!] piercings: [BodyModification!] + """ + The gallery, ordered as this viewer ranks image types. Anywhere one image + stands for the performer, that is `images[0]`: a card, a grid, a merge + target. + """ images: [Image!]! + """The same images, each with the types it has been labelled with here""" + typed_images: [TypedImage!]! + """ + The most recognisable image, for search results and dropdowns only. + + Always prefers a face crop and ignores the viewer's type preference: + legibility at thumbnail size is not a matter of taste, and being the same + for everyone is what lets it be cached. Everywhere else wants `images[0]`, + which does follow the viewer. + """ + thumbnail: Image deleted: Boolean! edits: [Edit!]! scene_count: Int! @@ -167,6 +183,11 @@ input PerformerCreateInput { tattoos: [BodyModificationInput!] piercings: [BodyModificationInput!] image_ids: [ID!] + """ + Labels for the images named. An image in image_ids with no entry here is + simply unlabelled; there is nothing to preserve on a create. + """ + image_types: [ImageAssignmentInput!] draft_id: ID } @@ -194,6 +215,17 @@ input PerformerUpdateInput { tattoos: [BodyModificationInput!] piercings: [BodyModificationInput!] image_ids: [ID!] + """ + Labels for the images named. Absent leaves every assignment untouched, an + empty list clears them all, and an image in image_ids with no entry here + keeps what it has. + + Explicit null behaves as absent and preserves, which differs from the edit + path, where it clears. This path is not told which fields the client stated, + so it cannot tell an omitted field from one set to null; the edit path is, + and does. Send an empty list to clear, on either path. + """ + image_types: [ImageAssignmentInput!] } input PerformerDestroyInput { @@ -223,6 +255,16 @@ input PerformerEditDetailsInput { tattoos: [BodyModificationInput!] piercings: [BodyModificationInput!] image_ids: [ID!] + """ + Labels for the images named. Omitting the field leaves assignments alone; + null or an empty list clears them all, matching image_ids. A non-empty list + is authoritative only over the images it names. + + Null clearing here and preserving on performerUpdate is not a rule, it is + what each path can see: this one is told which fields the client stated, and + that one is not. Send an empty list to clear, on either path. + """ + image_types: [ImageAssignmentInput!] draft_id: ID } @@ -270,6 +312,17 @@ type PerformerEdit { removed_piercings: [BodyModification!] added_images: [Image!] removed_images: [Image!] + """Label and date changes, one entry per affected image""" + image_changes: [ImageAssignmentChange!]! + """ + The gallery this edit results in: each surviving image with the labels and + date it will carry once applied. + + The state being voted on, as opposed to image_changes, which is what moves. + A reviewer opening an image wants to see what it will be, the same way the + gallery lightbox shows it. + """ + typed_images: [TypedImage!]! draft_id: ID aliases: [String!]! diff --git a/graphql/schema/types/user.graphql b/graphql/schema/types/user.graphql index 330fccb77..3a667db87 100644 --- a/graphql/schema/types/user.graphql +++ b/graphql/schema/types/user.graphql @@ -33,6 +33,10 @@ type User { """Should not be visible to other users""" api_key: String @isUserOwner notification_subscriptions: [NotificationEnum!]! @isUserOwner + """Preferred order of types within their group, when ranking images. Empty means no preference.""" + image_type_preferences: [ImageTypeEnum!]! @isUserOwner + """Preferred order of the groups themselves, deciding which dimension is compared first. Empty means the instance order.""" + image_type_group_preferences: [ImageTypeGroupEnum!]! @isUserOwner """ Vote counts by type """ vote_count: UserVoteCount! diff --git a/internal/api/crop_template_integration_test.go b/internal/api/crop_template_integration_test.go new file mode 100644 index 000000000..25b7eeb61 --- /dev/null +++ b/internal/api/crop_template_integration_test.go @@ -0,0 +1,173 @@ +//go:build integration + +package api_test + +import ( + "testing" + + "github.com/stashapp/stash-box/internal/image/croptemplate" + "github.com/stashapp/stash-box/internal/models" + "github.com/stretchr/testify/assert" +) + +// The frame is read through the vocabulary query, which is the call the edit +// form already makes, so the cropping tool costs no extra round trip + +func (s *imageTypeTestRunner) cropTemplateFor(key models.ImageTypeEnum) *models.CropTemplate { + s.t.Helper() + + for _, group := range s.readGroups() { + for _, imageType := range group.Types { + if imageType.Key != key { + continue + } + template, err := s.resolver.ImageType().CropTemplate(s.ctx, &imageType) + assert.NoError(s.t, err) + return template + } + } + + s.t.Fatalf("no image type %s in the vocabulary", key) + return nil +} + +func TestCropTypesCarryATemplate(t *testing.T) { + s := createImageTypeTestRunner(t) + + for _, key := range []models.ImageTypeEnum{ + models.ImageTypeEnumCropFace, + models.ImageTypeEnumCropBust, + models.ImageTypeEnumCropTorso, + models.ImageTypeEnumCropThreeQuarter, + models.ImageTypeEnumCropThreeQuarterPlus, + models.ImageTypeEnumCropFullBody, + models.ImageTypeEnumCropWide, + } { + t.Run(string(key), func(t *testing.T) { + template := s.cropTemplateFor(key) + if !assert.NotNil(t, template, "no crop template") { + return + } + + assert.Greater(t, template.AspectRatio, 0.0, "unusable aspect ratio") + assert.NotEmpty(t, template.Guides, "a template with no guides cannot draw an overlay") + + for i, guide := range template.Guides { + assert.Contains(t, + []models.CropGuideAxisEnum{models.CropGuideAxisEnumX, models.CropGuideAxisEnumY}, + guide.Axis, "guide %d has an unknown axis", i) + // A line outside the canvas would render outside the crop + // frame where it means nothing + assert.GreaterOrEqual(t, guide.Position, 0.0, "guide %d is off the canvas", i) + assert.LessOrEqual(t, guide.Position, 1.0, "guide %d is off the canvas", i) + } + }) + } +} + +// Everything else in the taxonomy describes the subject rather than the frame, +// and a pose or a state of dress has nothing to say about the shape of the +// picture. Offering those a crop frame would be offering nonsense +func TestNonCropTypesHaveNoTemplate(t *testing.T) { + s := createImageTypeTestRunner(t) + + for _, key := range []models.ImageTypeEnum{ + models.ImageTypeEnumShotPortrait, + models.ImageTypeEnumViewFront, + models.ImageTypeEnumPostureStanding, + models.ImageTypeEnumDressNonNude, + } { + t.Run(string(key), func(t *testing.T) { + assert.Nil(t, s.cropTemplateFor(key), "unexpected crop template") + }) + } +} + +func TestWideIsLandscapeAndTheRestArePortrait(t *testing.T) { + s := createImageTypeTestRunner(t) + + wide := s.cropTemplateFor(models.ImageTypeEnumCropWide) + if assert.NotNil(t, wide) { + assert.Greater(t, wide.AspectRatio, 1.0, "Wide should be a landscape frame") + } + + for _, key := range []models.ImageTypeEnum{ + models.ImageTypeEnumCropFace, + models.ImageTypeEnumCropFullBody, + } { + template := s.cropTemplateFor(key) + if assert.NotNil(t, template, key) { + assert.Less(t, template.AspectRatio, 1.0, "%s should be a portrait frame", key) + } + } +} + +// Labels are what make the overlay teach rather than decorate, so at least one +// guide should arrive named. Which one, and what it says, is the template's +// business +func TestCropTemplateGuidesAreLabelled(t *testing.T) { + s := createImageTypeTestRunner(t) + + template := s.cropTemplateFor(models.ImageTypeEnumCropFace) + if !assert.NotNil(t, template) { + return + } + + var labelled, roled int + for _, guide := range template.Guides { + if guide.Label != nil && *guide.Label != "" { + labelled++ + } + if guide.Role != nil { + assert.True(t, guide.Role.IsValid(), "role %q is not one of the known values", *guide.Role) + roled++ + } + } + + assert.NotZero(t, labelled, "no guide carries a label") + assert.NotZero(t, roled, "no guide says how closely it should be followed") +} + +func TestCropTemplateMatchesTheLoadedFile(t *testing.T) { + s := createImageTypeTestRunner(t) + + loader := croptemplate.NewLoader() + + for _, key := range []models.ImageTypeEnum{ + models.ImageTypeEnumCropFace, + models.ImageTypeEnumCropWide, + } { + t.Run(string(key), func(t *testing.T) { + loaded, ok := loader.Template(string(key)) + assert.True(t, ok, "the loader has no template") + + resolved := s.cropTemplateFor(key) + if !assert.NotNil(t, resolved) { + return + } + + assert.Equal(t, loaded.AspectRatio(), resolved.AspectRatio) + if !assert.Len(t, resolved.Guides, len(loaded.Guides)) { + return + } + + for i, want := range loaded.Guides { + got := resolved.Guides[i] + assert.EqualValues(t, want.Axis, got.Axis, "guide %d axis", i) + assert.Equal(t, want.Position, got.Position, "guide %d position", i) + + if want.Label == "" { + assert.Nil(t, got.Label, "guide %d label", i) + } else if assert.NotNil(t, got.Label, "guide %d label", i) { + assert.Equal(t, want.Label, *got.Label, "guide %d label", i) + } + + if want.Role == "" { + assert.Nil(t, got.Role, "guide %d role", i) + } else if assert.NotNil(t, got.Role, "guide %d role", i) { + assert.EqualValues(t, want.Role, *got.Role, "guide %d role", i) + } + } + }) + } +} diff --git a/internal/api/graphql_client_test.go b/internal/api/graphql_client_test.go index fa0c83472..15e869e22 100644 --- a/internal/api/graphql_client_test.go +++ b/internal/api/graphql_client_test.go @@ -128,6 +128,17 @@ func (t tagOutput) UUID() uuid.UUID { return uuid.FromStringOrNil(t.ID) } +type imageTypeOutput struct { + Key models.ImageTypeEnum `json:"key"` + SortOrder int `json:"sort_order"` +} + +type imageTypeGroupOutput struct { + Key models.ImageTypeGroupEnum `json:"key"` + SortOrder int `json:"sort_order"` + Types []imageTypeOutput `json:"types"` +} + type siteOutput struct { ID string `json:"id"` Name string `json:"name"` @@ -1143,6 +1154,29 @@ func (c *graphqlClient) amendEdit(input models.AmendEditInput) (bool, error) { return resp.AmendEdit.ID != uuid.Nil, nil } +func (c *graphqlClient) imageTypeOrderUpdate(input models.ImageTypeOrderInput) ([]imageTypeGroupOutput, error) { + q := ` + mutation ImageTypeOrderUpdate($input: ImageTypeOrderInput!) { + imageTypeOrderUpdate(input: $input) { + key + sort_order + types { + key + sort_order + } + } + }` + + var resp struct { + ImageTypeOrderUpdate []imageTypeGroupOutput `json:"imageTypeOrderUpdate"` + } + if err := c.Post(q, &resp, client.Var("input", input)); err != nil { + return nil, err + } + + return resp.ImageTypeOrderUpdate, nil +} + func (c *graphqlClient) updateEditComment(input models.UpdateEditCommentInput) (uuid.UUID, error) { q := ` mutation UpdateEditComment($input: UpdateEditCommentInput!) { diff --git a/internal/api/image_crop_integration_test.go b/internal/api/image_crop_integration_test.go new file mode 100644 index 000000000..c27dd660b --- /dev/null +++ b/internal/api/image_crop_integration_test.go @@ -0,0 +1,371 @@ +//go:build integration + +package api_test + +import ( + "bytes" + "image" + "image/color" + "image/jpeg" + "image/png" + "strconv" + "testing" + + "github.com/99designs/gqlgen/graphql" + "github.com/stashapp/stash-box/internal/models" + "github.com/stashapp/stash-box/internal/storage" + "github.com/stretchr/testify/assert" +) + +// Cropping happens on the server, so these go through imageCreate rather than +// testing the geometry in isolation: what matters is that the stored image +// (the one a reviewer will see and the one the checksum is taken from) is the +// frame that was asked for + +var cropImageSuffix int + +// uploadCropped sends a distinctly-coloured image with a crop attached +// +// Each call needs different pixels, because images are deduplicated on the md5 +// of their stored bytes and two identical uploads are one image by design +func (s *testRunner) uploadCropped(width, height int, crop *models.ImageCropInput) (*models.Image, error) { + s.t.Helper() + + cropImageSuffix++ + + img := image.NewRGBA(image.Rect(0, 0, width, height)) + for y := range height { + for x := range width { + // A gradient rather than a flat fill, so a crop of the wrong + // region is visible as different pixels rather than looking + // identical to the right one + img.Set(x, y, color.RGBA{ + R: uint8(x % 256), + G: uint8(y % 256), + B: uint8(cropImageSuffix), + A: 255, + }) + } + } + + var buf bytes.Buffer + if err := png.Encode(&buf, img); err != nil { + return nil, err + } + + return s.resolver.Mutation().ImageCreate(s.ctx, models.ImageCreateInput{ + File: &graphql.Upload{ + File: bytes.NewReader(buf.Bytes()), + Size: int64(buf.Len()), + Filename: "crop-" + strconv.Itoa(cropImageSuffix) + ".png", + }, + Crop: crop, + }) +} + +// storedPixel reads the image back out of the store and samples it at a +// fraction of the way across and down +// +// The stored file, not the response: dimensions are the one thing a crop of the +// wrong region gets right, so nothing short of looking at the pixels can tell +// the top-left quarter from the bottom-right one. PNG in means PNG out because +// exportCropped re-encodes in the format that came in, so the values are the +// source's exactly, and a tolerance would only be hiding something +func (s *testRunner) storedPixel(img *models.Image, fx, fy float64) color.RGBA { + s.t.Helper() + + reader, _, err := storage.Image().ReadFile(*img) + if err != nil { + s.t.Fatalf("reading the stored image: %v", err) + } + defer reader.Close() + + decoded, format, err := image.Decode(reader) + if err != nil { + s.t.Fatalf("decoding the stored image: %v", err) + } + if format != "png" { + s.t.Fatalf("stored as %s; this test reads exact pixels and needs a lossless one", format) + } + + bounds := decoded.Bounds() + x := bounds.Min.X + int(float64(bounds.Dx())*fx) + y := bounds.Min.Y + int(float64(bounds.Dy())*fy) + + r, g, b, a := decoded.At(x, y).RGBA() + return color.RGBA{R: uint8(r >> 8), G: uint8(g >> 8), B: uint8(b >> 8), A: uint8(a >> 8)} +} + +func TestImageCreateCropsToTheRequestedFrame(t *testing.T) { + s := asAdmin(t) + + for _, tc := range []struct { + name string + crop models.ImageCropInput + width, height int + }{ + {"the top-left quarter", + models.ImageCropInput{X: 0, Y: 0, Width: 0.5, Height: 0.5}, 200, 300}, + {"the bottom-right quarter", + models.ImageCropInput{X: 0.5, Y: 0.5, Width: 0.5, Height: 0.5}, 200, 300}, + {"a 2:3 frame out of a square", + models.ImageCropInput{X: 0.25, Y: 0, Width: 0.5, Height: 0.75}, 400, 400}, + } { + t.Run(tc.name, func(t *testing.T) { + img, err := s.uploadCropped(tc.width, tc.height, &tc.crop) + if !assert.NoError(t, err) { + return + } + + assert.Equal(t, int(tc.crop.Width*float64(tc.width)), img.Width) + assert.Equal(t, int(tc.crop.Height*float64(tc.height)), img.Height) + + // Which region, not just how big. uploadCropped paints R from the + // column and G from the row, so the middle of the stored crop names + // the source pixel it was taken from. The two quarters below + // differ by 100 and 150, far outside anything an encoder could do + middle := s.storedPixel(img, 0.5, 0.5) + + sourceX := int(tc.crop.X*float64(tc.width)) + img.Width/2 + sourceY := int(tc.crop.Y*float64(tc.height)) + img.Height/2 + + assert.Equal(t, uint8(sourceX%256), middle.R, + "the crop came from column %d, not %d", int(middle.R), sourceX) + assert.Equal(t, uint8(sourceY%256), middle.G, + "the crop came from row %d, not %d", int(middle.G), sourceY) + }) + } +} + +// A crop that keeps everything should not re-encode: an upload nobody actually +// cropped must not pay a generation of quality for nothing +// +// Checked by deduplication rather than by dimensions, because a re-encode +// preserves the dimensions perfectly well. The same bytes uploaded with and +// without a full-frame crop have to land as one image, which they only can if +// the crop was skipped rather than performed +func TestImageCreateLeavesAFullFrameAlone(t *testing.T) { + s := asAdmin(t) + + source := image.NewRGBA(image.Rect(0, 0, 120, 180)) + for y := range 180 { + for x := range 120 { + source.Set(x, y, color.RGBA{R: uint8(x % 256), G: uint8(y % 256), B: 31, A: 255}) + } + } + var buf bytes.Buffer + assert.NoError(t, png.Encode(&buf, source)) + + upload := func(crop *models.ImageCropInput) (*models.Image, error) { + return s.resolver.Mutation().ImageCreate(s.ctx, models.ImageCreateInput{ + File: &graphql.Upload{ + File: bytes.NewReader(buf.Bytes()), + Size: int64(buf.Len()), + Filename: "identity.png", + }, + Crop: crop, + }) + } + + plain, err := upload(nil) + if !assert.NoError(t, err) { + return + } + whole, err := upload(&models.ImageCropInput{X: 0, Y: 0, Width: 1, Height: 1}) + if !assert.NoError(t, err) { + return + } + + assert.Equal(t, plain.ID, whole.ID, + "a full-frame crop re-encoded the upload instead of leaving it alone") + assert.Equal(t, 120, whole.Width) + assert.Equal(t, 180, whole.Height) +} + +// The property a browser crop would have lost: deduplication is on a checksum +// of the stored bytes, so the same source and the same frame have to produce +// the same bytes. We can only guarantee this by using one encoder, on one machine +func TestIdenticalCropsDeduplicate(t *testing.T) { + s := asAdmin(t) + + source := image.NewRGBA(image.Rect(0, 0, 300, 400)) + for y := range 400 { + for x := range 300 { + source.Set(x, y, color.RGBA{R: uint8(x % 256), G: uint8(y % 256), B: 7, A: 255}) + } + } + var buf bytes.Buffer + assert.NoError(t, png.Encode(&buf, source)) + + upload := func(crop models.ImageCropInput) (*models.Image, error) { + return s.resolver.Mutation().ImageCreate(s.ctx, models.ImageCreateInput{ + File: &graphql.Upload{ + File: bytes.NewReader(buf.Bytes()), + Size: int64(buf.Len()), + Filename: "dedupe.png", + }, + Crop: &crop, + }) + } + + frame := models.ImageCropInput{X: 0.1, Y: 0.2, Width: 0.5, Height: 0.5} + first, err := upload(frame) + if !assert.NoError(t, err) { + return + } + second, err := upload(frame) + if !assert.NoError(t, err) { + return + } + assert.Equal(t, first.ID, second.ID, "the same source and frame should be one image") + + // A different frame is a different image, or dedupe would be collapsing + // things it should not + other, err := upload(models.ImageCropInput{X: 0.4, Y: 0.2, Width: 0.5, Height: 0.5}) + if assert.NoError(t, err) { + assert.NotEqual(t, first.ID, other.ID, "a different frame should be a different image") + } +} + +// Rotation grows the canvas to fit the turned image, and the frame is measured +// against that larger canvas. A quarter turn is the case with an exact answer: +// a 200x300 image becomes 300x200 +func TestImageCreateRotatesBeforeCropping(t *testing.T) { + s := asAdmin(t) + + quarter := 90.0 + img, err := s.uploadCropped(200, 300, &models.ImageCropInput{ + X: 0, Y: 0, Width: 1, Height: 1, Angle: &quarter, + }) + if !assert.NoError(t, err) { + return + } + + assert.Equal(t, 300, img.Width, "a quarter turn should swap the sides") + assert.Equal(t, 200, img.Height, "a quarter turn should swap the sides") +} + +// A small angle grows the canvas rather than clipping the corners off, which is +// what lets a contributor straighten a horizon and still crop inside the result +func TestRotationGrowsTheCanvas(t *testing.T) { + s := asAdmin(t) + + tilt := 10.0 + img, err := s.uploadCropped(200, 300, &models.ImageCropInput{ + X: 0, Y: 0, Width: 1, Height: 1, Angle: &tilt, + }) + if !assert.NoError(t, err) { + return + } + + assert.Greater(t, img.Width, 200, "the canvas should have grown to fit the rotation") + assert.Greater(t, img.Height, 300, "the canvas should have grown to fit the rotation") +} + +func TestImageCreateRejectsUnusableFrames(t *testing.T) { + s := asAdmin(t) + + tooFar := 180.0 + for _, tc := range []struct { + name string + crop models.ImageCropInput + }{ + {"no area", models.ImageCropInput{X: 0, Y: 0, Width: 0, Height: 1}}, + {"runs off the edge", models.ImageCropInput{X: 0.8, Y: 0, Width: 0.5, Height: 1}}, + {"negative origin", models.ImageCropInput{X: -0.1, Y: 0, Width: 0.5, Height: 1}}, + {"turned too far", models.ImageCropInput{X: 0, Y: 0, Width: 1, Height: 1, Angle: &tooFar}}, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := s.uploadCropped(200, 300, &tc.crop) + assert.Error(t, err, "an unusable frame should be refused") + }) + } +} + +// Uploading without a crop has to keep working exactly as it did: every image +// in every existing instance arrived that way, and most still will +func TestImageCreateWithoutACropIsUnchanged(t *testing.T) { + s := asAdmin(t) + + img, err := s.createTestImage(150, 250) + if !assert.NoError(t, err) { + return + } + assert.Equal(t, 150, img.Width) + assert.Equal(t, 250, img.Height) +} + +// exifOrientedJPEG encodes an image and declares an EXIF orientation for it, +// which is how a photograph off a phone arrives: the pixels are stored one way +// and a tag says which way up they go +// +// The APP1 segment is assembled by hand rather than pulled in from a library, +// because one tag in one IFD is a couple of dozen bytes and a dependency for +// that would be worse than the bytes +func exifOrientedJPEG(t *testing.T, img image.Image, orientation uint16) []byte { + t.Helper() + + var encoded bytes.Buffer + if err := jpeg.Encode(&encoded, img, &jpeg.Options{Quality: 90}); err != nil { + t.Fatalf("encoding: %v", err) + } + raw := encoded.Bytes() + + // A little-endian TIFF header, one IFD entry, and no next IFD. + tiff := []byte{ + 'I', 'I', 0x2a, 0x00, // byte order, and 42 + 0x08, 0x00, 0x00, 0x00, // IFD0 starts 8 bytes in + 0x01, 0x00, // one entry + 0x12, 0x01, // tag 0x0112, Orientation + 0x03, 0x00, // type 3, SHORT + 0x01, 0x00, 0x00, 0x00, // one value + byte(orientation), byte(orientation >> 8), 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, // no next IFD + } + + payload := append([]byte("Exif\x00\x00"), tiff...) + // The length covers itself but not the marker. + length := len(payload) + 2 + + out := make([]byte, 0, len(raw)+length+2) + out = append(out, raw[:2]...) // SOI + out = append(out, 0xff, 0xe1, byte(length>>8), byte(length)) + out = append(out, payload...) + out = append(out, raw[2:]...) + return out +} + +// Orientation 6 means the stored pixels are turned a quarter turn from how the +// image should be shown, which is what a phone held sideways produces. The +// client sends coordinates for what its browser drew, so the server has to put +// the image the same way up before measuring anything +// +// A tall 200x300 source displays as a wide 300x200. Its left half is therefore +// 150x200 and would be 100x300 if the tag were ignored, which is the bug this exists +// to catch +func TestImageCreateHonoursEXIFOrientation(t *testing.T) { + s := asAdmin(t) + + source := image.NewRGBA(image.Rect(0, 0, 200, 300)) + for y := range 300 { + for x := range 200 { + source.Set(x, y, color.RGBA{R: uint8(x % 256), G: uint8(y % 256), B: 91, A: 255}) + } + } + data := exifOrientedJPEG(t, source, 6) + + img, err := s.resolver.Mutation().ImageCreate(s.ctx, models.ImageCreateInput{ + File: &graphql.Upload{ + File: bytes.NewReader(data), + Size: int64(len(data)), + Filename: "sideways.jpg", + }, + Crop: &models.ImageCropInput{X: 0, Y: 0, Width: 0.5, Height: 1}, + }) + if !assert.NoError(t, err) { + return + } + + assert.Equal(t, 150, img.Width, "the orientation tag was ignored; a phone photo would crop sideways") + assert.Equal(t, 200, img.Height, "the orientation tag was ignored; a phone photo would crop sideways") +} diff --git a/internal/api/image_type_integration_test.go b/internal/api/image_type_integration_test.go new file mode 100644 index 000000000..12abc427c --- /dev/null +++ b/internal/api/image_type_integration_test.go @@ -0,0 +1,390 @@ +//go:build integration + +package api_test + +import ( + "strings" + "testing" + + "github.com/stashapp/stash-box/internal/models" + "github.com/stretchr/testify/assert" +) + +type imageTypeTestRunner struct { + testRunner +} + +func createImageTypeTestRunner(t *testing.T) *imageTypeTestRunner { + return &imageTypeTestRunner{ + testRunner: *asAdmin(t), + } +} + +func (s *imageTypeTestRunner) readGroups() []models.ImageTypeGroup { + s.t.Helper() + groups, err := s.resolver.Query().ImageTypeGroups(s.ctx, nil, nil) + assert.NoError(s.t, err) + return groups +} + +// orderInputFor turns a read of the vocabulary back into the input that would +// reproduce it, so a test can restore whatever order it found. +func orderInputFor(groups []models.ImageTypeGroup) models.ImageTypeOrderInput { + input := models.ImageTypeOrderInput{} + for _, group := range groups { + input.Groups = append(input.Groups, group.Key) + for _, imageType := range group.Types { + input.Types = append(input.Types, imageType.Key) + } + } + return input +} + +// reversedOrderInput reverses the groups, and the types within each group. +func reversedOrderInput(groups []models.ImageTypeGroup) models.ImageTypeOrderInput { + input := models.ImageTypeOrderInput{} + for i := len(groups) - 1; i >= 0; i-- { + group := groups[i] + input.Groups = append(input.Groups, group.Key) + for j := len(group.Types) - 1; j >= 0; j-- { + input.Types = append(input.Types, group.Types[j].Key) + } + } + return input +} + +// restoreOrder puts the vocabulary back, so ordering tests do not leak into +// the rest of the suite. +func (s *imageTypeTestRunner) restoreOrder(groups []models.ImageTypeGroup) { + _, err := s.resolver.Mutation().ImageTypeOrderUpdate(s.ctx, orderInputFor(groups)) + assert.NoError(s.t, err) +} + +func (s *imageTypeTestRunner) testImageTypeGroups() { + groups, err := s.resolver.Query().ImageTypeGroups(s.ctx, nil, nil) + assert.NoError(s.t, err) + + groupKeys := make([]models.ImageTypeGroupEnum, len(groups)) + for i, group := range groups { + groupKeys[i] = group.Key + } + assert.Equal(s.t, []models.ImageTypeGroupEnum{ + models.ImageTypeGroupEnumShot, + models.ImageTypeGroupEnumCrop, + models.ImageTypeGroupEnumView, + models.ImageTypeGroupEnumPosture, + models.ImageTypeGroupEnumDress, + }, groupKeys, "groups should come back in seeded priority order") + + seededTypes := 0 + for _, group := range groups { + assert.True(s.t, group.Exclusive, "every seeded group is exclusive: %s", group.Key) + assert.NotEmpty(s.t, group.Types) + + for i, imageType := range group.Types { + // The seed numbers each group's types densely from zero, so + // position and sort_order agree only if both are right. + assert.Equal(s.t, i, imageType.SortOrder, "type %s out of order", imageType.Key) + + assert.Equal(s.t, []models.ImageTypeScopeEnum{models.ImageTypeScopeEnumPerformer}, + imageType.ValidTypes, "phase 1 seeds performer types only: %s", imageType.Key) + } + + seededTypes += len(group.Types) + } + + assert.Equal(s.t, 25, seededTypes) +} + +func (s *imageTypeTestRunner) testImageTypeGroupsByTarget() { + performer := models.ImageTypeScopeEnumPerformer + performerGroups, err := s.resolver.Query().ImageTypeGroups(s.ctx, &performer, nil) + assert.NoError(s.t, err) + assert.Len(s.t, performerGroups, 5) + + // Every seeded type is performer-only, so filtering by scene empties every + // group, and an empty group is dropped rather than returned bare. + scene := models.ImageTypeScopeEnumScene + sceneGroups, err := s.resolver.Query().ImageTypeGroups(s.ctx, &scene, nil) + assert.NoError(s.t, err) + assert.Empty(s.t, sceneGroups) +} + +// The seeded rows and the GraphQL enums are two representations of one truth. +// This is what stops them drifting apart. +func (s *imageTypeTestRunner) testImageTypeSeedMatchesSchema() { + groups, err := s.resolver.Query().ImageTypeGroups(s.ctx, nil, nil) + assert.NoError(s.t, err) + + var groupKeys []models.ImageTypeGroupEnum + var typeKeys []models.ImageTypeEnum + + for _, group := range groups { + groupKeys = append(groupKeys, group.Key) + + for _, imageType := range group.Types { + typeKeys = append(typeKeys, imageType.Key) + + assert.True(s.t, strings.HasPrefix(string(imageType.Key), string(group.Key)+"_"), + "type %s must be prefixed with its group key %s", imageType.Key, group.Key) + } + } + + // ElementsMatch fails on extras in either direction, which is the point: + // a seeded row with no enum value is as broken as the reverse. + assert.ElementsMatch(s.t, models.AllImageTypeGroupEnum, groupKeys) + assert.ElementsMatch(s.t, models.AllImageTypeEnum, typeKeys) +} + +func (s *imageTypeTestRunner) testImageTypeOrderUpdate() { + before := s.readGroups() + defer s.restoreOrder(before) + + // Reversing guarantees the transaction passes through states where two + // rows share a sort_order -- DRESS taking 0 while SHOT still holds it. + // That survives only because both unique constraints are deferred and the + // whole reorder commits once; a statement-per-transaction implementation + // would abort here. + reversed := reversedOrderInput(before) + + returned, err := s.resolver.Mutation().ImageTypeOrderUpdate(s.ctx, reversed) + assert.NoError(s.t, err) + + for _, groups := range [][]models.ImageTypeGroup{returned, s.readGroups()} { + if !assert.Len(s.t, groups, len(before)) { + continue + } + + for i, group := range groups { + original := before[len(before)-1-i] + + assert.Equal(s.t, original.Key, group.Key) + assert.Equal(s.t, i, group.SortOrder) + + if !assert.Len(s.t, group.Types, len(original.Types)) { + continue + } + for j, imageType := range group.Types { + assert.Equal(s.t, original.Types[len(original.Types)-1-j].Key, imageType.Key) + assert.Equal(s.t, j, imageType.SortOrder) + } + } + } +} + +func (s *imageTypeTestRunner) testImageTypeOrderUpdateRequiresAdmin() { + before := s.readGroups() + + // Through the client, so the @hasRole(ADMIN) directive actually runs; + // calling the resolver directly would bypass it. + reader := asRead(s.t) + _, err := reader.client.imageTypeOrderUpdate(reversedOrderInput(before)) + assert.Error(s.t, err) + assert.Contains(s.t, err.Error(), "not authorized") + + assert.Equal(s.t, orderInputFor(before), orderInputFor(s.readGroups())) +} + +func (s *imageTypeTestRunner) testImageTypeOrderUpdateRejectsPartial() { + before := s.readGroups() + complete := orderInputFor(before) + + // The expected message matters as much as the error. A partial list also + // happens to collide on sort_order at commit, so asserting only that + // something failed would pass even with the completeness check removed -- + // and would report a constraint violation where an admin needs to be told + // what was missing. + testCases := []struct { + name string + input models.ImageTypeOrderInput + contains string + }{ + {"a group missing", models.ImageTypeOrderInput{ + Groups: complete.Groups[1:], + Types: complete.Types, + }, "groups must list all 5 values"}, + {"a type missing", models.ImageTypeOrderInput{ + Groups: complete.Groups, + Types: complete.Types[1:], + }, "types must list all 25 values"}, + {"a group repeated in place of another", models.ImageTypeOrderInput{ + Groups: append([]models.ImageTypeGroupEnum{complete.Groups[0]}, complete.Groups[:len(complete.Groups)-1]...), + Types: complete.Types, + }, "more than once"}, + {"empty lists", models.ImageTypeOrderInput{}, "groups must list all 5 values"}, + } + + for _, testCase := range testCases { + _, err := s.resolver.Mutation().ImageTypeOrderUpdate(s.ctx, testCase.input) + if assert.Error(s.t, err, "should reject %s", testCase.name) { + assert.ErrorContains(s.t, err, testCase.contains, "wrong rejection for %s", testCase.name) + } + + // Rejected outright rather than partly applied. + assert.Equal(s.t, complete, orderInputFor(s.readGroups()), "order changed after %s", testCase.name) + } +} + +func TestImageTypeOrderUpdate(t *testing.T) { + it := createImageTypeTestRunner(t) + it.testImageTypeOrderUpdate() +} + +func TestImageTypeOrderUpdateRequiresAdmin(t *testing.T) { + it := createImageTypeTestRunner(t) + it.testImageTypeOrderUpdateRequiresAdmin() +} + +func TestImageTypeOrderUpdateRejectsPartial(t *testing.T) { + it := createImageTypeTestRunner(t) + it.testImageTypeOrderUpdateRejectsPartial() +} + +func TestImageTypeGroups(t *testing.T) { + it := createImageTypeTestRunner(t) + it.testImageTypeGroups() +} + +func TestImageTypeGroupsByTarget(t *testing.T) { + it := createImageTypeTestRunner(t) + it.testImageTypeGroupsByTarget() +} + +func TestImageTypeSeedMatchesSchema(t *testing.T) { + it := createImageTypeTestRunner(t) + it.testImageTypeSeedMatchesSchema() +} + +// image_type_conflicts stores each pair once, and the service mirrors it, so a +// client need not know which side of a pair it is holding. +// +// This is a guarantee something depends on rather than a nicety. The editor +// reads conflicts_with in one direction only -- ImageLabels blocks an option by +// looking at the conflicts of the types already chosen, never at the option's +// own -- so if the field stopped being served both ways a conflict would block +// from one side and not the other, and only from whichever side the seed +// happened to name. The component test cannot see this: it is handed a +// vocabulary, and the shape of that vocabulary is decided here. +func (s *imageTypeTestRunner) testConflictsAreServedBothWaysRound() { + conflicts := map[models.ImageTypeEnum][]models.ImageTypeEnum{} + for _, group := range s.readGroups() { + for _, imageType := range group.Types { + conflicts[imageType.Key] = imageType.ConflictsWith + } + } + + // Seeded as ('CROP_FACE', 'DRESS_TOPLESS') and nothing else, so the reverse + // only exists if it was mirrored. + assert.Contains(s.t, conflicts[models.ImageTypeEnumCropFace], + models.ImageTypeEnumDressTopless, "the seeded direction is missing") + assert.Contains(s.t, conflicts[models.ImageTypeEnumDressTopless], + models.ImageTypeEnumCropFace, "the seeded pair is served one way round only") + + // And every pair, so a row added later cannot arrive one-sided. + pairs := 0 + for key, against := range conflicts { + for _, other := range against { + pairs++ + assert.Contains(s.t, conflicts[other], key, + "%s conflicts with %s, but not the other way round", key, other) + } + } + assert.NotZero(s.t, pairs, "no conflicts in the vocabulary; this test proves nothing") +} + +func TestImageTypeConflictsAreServedBothWaysRound(t *testing.T) { + it := createImageTypeTestRunner(t) + it.testConflictsAreServedBothWaysRound() +} + +// restoreEnabled switches the whole vocabulary back on, so an enabling test +// does not leak into the rest of the suite. +func (s *imageTypeTestRunner) restoreEnabled() { + _, err := s.resolver.Mutation().ImageTypeSetEnabled(s.ctx, models.ImageTypeEnabledInput{}) + assert.NoError(s.t, err) +} + +// Disabling hides a type from everyone who asks what may be used, while the +// admin screen keeps seeing it -- otherwise there would be no way back. +func (s *imageTypeTestRunner) testDisabledTypeIsHiddenButRecoverable() { + defer s.restoreEnabled() + + _, err := s.resolver.Mutation().ImageTypeSetEnabled(s.ctx, models.ImageTypeEnabledInput{ + DisabledTypes: []models.ImageTypeEnum{models.ImageTypeEnumShotCandid}, + }) + assert.NoError(s.t, err) + + keysIn := func(groups []models.ImageTypeGroup) []models.ImageTypeEnum { + var keys []models.ImageTypeEnum + for _, group := range groups { + for _, imageType := range group.Types { + keys = append(keys, imageType.Key) + } + } + return keys + } + + visible, err := s.resolver.Query().ImageTypeGroups(s.ctx, nil, nil) + assert.NoError(s.t, err) + assert.NotContains(s.t, keysIn(visible), models.ImageTypeEnumShotCandid) + assert.Contains(s.t, keysIn(visible), models.ImageTypeEnumShotPortrait, + "only the disabled type goes, not its group") + + includeDisabled := true + all, err := s.resolver.Query().ImageTypeGroups(s.ctx, nil, &includeDisabled) + assert.NoError(s.t, err) + assert.Contains(s.t, keysIn(all), models.ImageTypeEnumShotCandid, + "the admin screen has to see what it switched off") + + for _, group := range all { + for _, imageType := range group.Types { + if imageType.Key == models.ImageTypeEnumShotCandid { + assert.False(s.t, imageType.Enabled) + } + } + } +} + +// A group being off takes its types with it, without their own flags being +// rewritten -- so switching the group back on restores exactly what was there. +func (s *imageTypeTestRunner) testDisabledGroupHidesItsTypes() { + defer s.restoreEnabled() + + _, err := s.resolver.Mutation().ImageTypeSetEnabled(s.ctx, models.ImageTypeEnabledInput{ + DisabledGroups: []models.ImageTypeGroupEnum{models.ImageTypeGroupEnumPosture}, + }) + assert.NoError(s.t, err) + + visible, err := s.resolver.Query().ImageTypeGroups(s.ctx, nil, nil) + assert.NoError(s.t, err) + for _, group := range visible { + assert.NotEqual(s.t, models.ImageTypeGroupEnumPosture, group.Key) + } + assert.Len(s.t, visible, 4, "the other four are untouched") + + includeDisabled := true + all, err := s.resolver.Query().ImageTypeGroups(s.ctx, nil, &includeDisabled) + assert.NoError(s.t, err) + assert.Len(s.t, all, 5) + + for _, group := range all { + if group.Key != models.ImageTypeGroupEnumPosture { + continue + } + assert.False(s.t, group.Enabled) + for _, imageType := range group.Types { + assert.True(s.t, imageType.Enabled, + "a group being off must not rewrite its types' own flags: %s", imageType.Key) + } + } +} + +func TestDisabledTypeIsHiddenButRecoverable(t *testing.T) { + s := createImageTypeTestRunner(t) + s.testDisabledTypeIsHiddenButRecoverable() +} + +func TestDisabledGroupHidesItsTypes(t *testing.T) { + s := createImageTypeTestRunner(t) + s.testDisabledGroupHidesItsTypes() +} diff --git a/internal/api/integration_test.go b/internal/api/integration_test.go index c34bfe87a..6bb198f16 100644 --- a/internal/api/integration_test.go +++ b/internal/api/integration_test.go @@ -3,13 +3,19 @@ package api_test import ( + "bytes" "context" + "image" + "image/color" + "image/png" "net/http" + "os" "strconv" "testing" "github.com/stashapp/stash-box/internal/api" "github.com/stashapp/stash-box/internal/auth" + "github.com/stashapp/stash-box/internal/config" dbtest "github.com/stashapp/stash-box/internal/database/testutil" "github.com/stashapp/stash-box/internal/dataloader" "github.com/stashapp/stash-box/internal/models" @@ -152,6 +158,19 @@ func (p *userPopulator) PopulateDB(factory *service.Factory) error { } func TestMain(m *testing.M) { + // Images are stored files, not remote URLs: the frontend only ever uploads, + // and Image.url is resolved to a local path rather than read from the + // column. Tests never load a config file, so point the backend at a temp + // directory or every upload hits a nil storage backend. + imageDir, err := os.MkdirTemp("", "stash-box-test-images") + if err != nil { + panic(err) + } + defer os.RemoveAll(imageDir) + + config.C.ImageBackend = string(config.FileBackend) + config.C.ImageLocation = imageDir + userDB = &userPopulator{} dbtest.TestWithDatabase(m, userDB) } @@ -174,6 +193,34 @@ var categorySuffix int var siteSuffix int var siteCategorySuffix int +var imageSuffix int + +// createTestImage uploads a small generated PNG, which is how images are +// really made: images are deduplicated by the md5 of their content, so each +// call needs distinct pixels to produce a distinct image. Width and height +// vary too, since that is what the aspect-ratio comparators sort on. +func (s *testRunner) createTestImage(width, height int) (*models.Image, error) { + s.t.Helper() + + imageSuffix++ + + img := image.NewRGBA(image.Rect(0, 0, width, height)) + img.Set(0, 0, color.RGBA{R: uint8(imageSuffix), G: uint8(imageSuffix >> 8), A: 255}) + + var buf bytes.Buffer + if err := png.Encode(&buf, img); err != nil { + return nil, err + } + + return s.resolver.Mutation().ImageCreate(s.ctx, models.ImageCreateInput{ + File: &graphql.Upload{ + File: bytes.NewReader(buf.Bytes()), + Size: int64(buf.Len()), + Filename: "test-" + strconv.Itoa(imageSuffix) + ".png", + }, + }) +} + func createTestRunner(t *testing.T, u *models.User, roles []models.RoleEnum) *testRunner { resolver := api.NewResolver(*dbtest.Factory()) diff --git a/internal/api/performer_edit_image_type_integration_test.go b/internal/api/performer_edit_image_type_integration_test.go new file mode 100644 index 000000000..636a83c8d --- /dev/null +++ b/internal/api/performer_edit_image_type_integration_test.go @@ -0,0 +1,723 @@ +//go:build integration + +package api_test + +import ( + "testing" + + "github.com/gofrs/uuid" + dbtest "github.com/stashapp/stash-box/internal/database/testutil" + "github.com/stashapp/stash-box/internal/models" + "github.com/stretchr/testify/assert" +) + +type performerEditImageTypeTestRunner struct { + performerImageTypeTestRunner +} + +func createPerformerEditImageTypeTestRunner(t *testing.T) *performerEditImageTypeTestRunner { + return &performerEditImageTypeTestRunner{ + performerImageTypeTestRunner: *createPerformerImageTypeTestRunner(t), + } +} + +// applyPerformerEdit submits a modify edit and approves it. +func (s *performerEditImageTypeTestRunner) applyPerformerEdit(performerID uuid.UUID, details *models.PerformerEditDetailsInput) { + s.t.Helper() + + edit, err := s.createTestPerformerEdit( + models.OperationEnumModify, + details, + &models.EditInput{Operation: models.OperationEnumModify, ID: &performerID}, + nil, + ) + // Returns rather than reads through the nil edit, so a refused edit fails + // this one test instead of panicking the whole package run. + if !assert.NoError(s.t, err) { + return + } + + _, err = s.approveEdit(edit.ID) + assert.NoError(s.t, err) +} + +func (s *performerEditImageTypeTestRunner) testEditAddsImageWithLabels() { + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + image, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + s.applyPerformerEdit(performerID, &models.PerformerEditDetailsInput{ + ImageIds: []uuid.UUID{image.ID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: image.ID, Types: []models.ImageTypeEnum{ + models.ImageTypeEnumShotPortrait, + models.ImageTypeEnumCropFace, + }}, + }, + }) + + assert.Equal(s.t, []models.ImageTypeEnum{ + models.ImageTypeEnumShotPortrait, + models.ImageTypeEnumCropFace, + }, s.typesOf(performerID)[image.ID]) +} + +// Retagging adds one tuple and removes another; no image is added or removed. +func (s *performerEditImageTypeTestRunner) testEditRetagsExistingImage() { + performerID, imageID := s.createLabelledPerformer( + models.ImageTypeEnumShotPortrait, models.ImageTypeEnumCropFace) + + s.applyPerformerEdit(performerID, &models.PerformerEditDetailsInput{ + ImageIds: []uuid.UUID{imageID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: imageID, Types: []models.ImageTypeEnum{ + models.ImageTypeEnumShotCandid, + models.ImageTypeEnumCropFace, + }}, + }, + }) + + assert.Equal(s.t, []models.ImageTypeEnum{ + models.ImageTypeEnumShotCandid, + models.ImageTypeEnumCropFace, + }, s.typesOf(performerID)[imageID]) +} + +func (s *performerEditImageTypeTestRunner) testEditRemovingImageDropsItsLabels() { + kept, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + dropped, err := s.createTestImage(600, 400) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{kept.ID, dropped.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + s.assign(performerID, + models.ImageTypeAssignment{ImageID: kept.ID, Type: models.ImageTypeEnumCropFace}, + models.ImageTypeAssignment{ImageID: dropped.ID, Type: models.ImageTypeEnumCropWide}, + ) + + // image_ids drops one image; image_types says nothing at all. The resolve + // query restricts assignments to the edit's resulting image set, so the + // dropped image's labels go with it. + s.applyPerformerEdit(performerID, &models.PerformerEditDetailsInput{ + ImageIds: []uuid.UUID{kept.ID}, + }) + + after := s.typesOf(performerID) + assert.Len(s.t, after, 1) + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumCropFace}, after[kept.ID]) + assert.Empty(s.t, after[dropped.ID]) +} + +// An edit submitted before this feature has no *_image_types keys at all. +// Applying it must not disturb labels applied in the meantime -- the absent-key +// path, through both copies of the final_images CTE chain. +func (s *performerEditImageTypeTestRunner) testEditSubmittedBeforeLabelsExisted() { + image, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{image.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + // Submitted while the performer is unlabelled, so the payload carries no + // image type keys whatsoever. + newName := s.generatePerformerName() + edit, err := s.createTestPerformerEdit( + models.OperationEnumModify, + &models.PerformerEditDetailsInput{Name: &newName}, + &models.EditInput{Operation: models.OperationEnumModify, ID: &performerID}, + nil, + ) + assert.NoError(s.t, err) + + // Labels arrive after submission but before the edit is applied. + s.assign(performerID, labels(image.ID, models.ImageTypeEnumShotDetail)...) + + _, err = s.approveEdit(edit.ID) + assert.NoError(s.t, err) + + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumShotDetail}, + s.typesOf(performerID)[image.ID], + "an edit predating image types must not clear labels applied since") +} + +// The image GC parses pending-edit JSON to decide what is unreferenced. The +// new payload keys must not disturb that. +func (s *performerEditImageTypeTestRunner) testPendingEditProtectsImage() { + image, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + unused, err := dbtest.Factory().Image().IsUnused(s.ctx, image.ID) + assert.NoError(s.t, err) + assert.True(s.t, unused, "an image on nothing is unused") + + name := s.generatePerformerName() + _, err = s.createTestPerformerEdit( + models.OperationEnumCreate, + &models.PerformerEditDetailsInput{ + Name: &name, + ImageIds: []uuid.UUID{image.ID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: image.ID, Types: []models.ImageTypeEnum{models.ImageTypeEnumCropFace}}, + }, + }, + nil, + nil, + ) + assert.NoError(s.t, err) + + unused, err = dbtest.Factory().Image().IsUnused(s.ctx, image.ID) + assert.NoError(s.t, err) + assert.False(s.t, unused, "an image referenced only by a pending edit must be protected") +} + +// Labels merge rather than clobber: each edit is resolved against current +// state at apply time, so the second to land keeps the first's work. +func (s *performerEditImageTypeTestRunner) testConcurrentLabellingMerges() { + first, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + second, err := s.createTestImage(600, 400) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{first.ID, second.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + bothImages := []uuid.UUID{first.ID, second.ID} + + // The first image already carries a label. This is what makes the test + // bite: a diff treating image_types as authoritative over the whole + // gallery would emit a tuple removing it from any edit that does not + // restate it. + s.assign(performerID, labels(first.ID, models.ImageTypeEnumShotPortrait)...) + + // Both editors submit before either is approved. A refines the labelled + // image; B labels the other one and names only it. + editA, err := s.createTestPerformerEdit( + models.OperationEnumModify, + &models.PerformerEditDetailsInput{ + ImageIds: bothImages, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: first.ID, Types: []models.ImageTypeEnum{ + models.ImageTypeEnumShotPortrait, + models.ImageTypeEnumCropFace, + }}, + }, + }, + &models.EditInput{Operation: models.OperationEnumModify, ID: &performerID}, + nil, + ) + assert.NoError(s.t, err) + + editB, err := s.createTestPerformerEdit( + models.OperationEnumModify, + &models.PerformerEditDetailsInput{ + ImageIds: bothImages, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: second.ID, Types: []models.ImageTypeEnum{models.ImageTypeEnumCropWide}}, + }, + }, + &models.EditInput{Operation: models.OperationEnumModify, ID: &performerID}, + nil, + ) + assert.NoError(s.t, err) + + _, err = s.approveEdit(editA.ID) + assert.NoError(s.t, err) + _, err = s.approveEdit(editB.ID) + assert.NoError(s.t, err) + + after := s.typesOf(performerID) + assert.Equal(s.t, []models.ImageTypeEnum{ + models.ImageTypeEnumShotPortrait, + models.ImageTypeEnumCropFace, + }, after[first.ID], "the second edit to land should keep the first's labels") + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumCropWide}, after[second.ID]) +} + +// Merges carry the sources' labels in the submitted input, exactly as their +// images already do. Nothing unions at apply time. +func (s *performerEditImageTypeTestRunner) testMergeCarriesSubmittedLabels() { + targetImage, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + sourceImage, err := s.createTestImage(600, 400) + assert.NoError(s.t, err) + + target, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{targetImage.ID}, + }) + assert.NoError(s.t, err) + targetID := target.UUID() + s.assign(targetID, labels(targetImage.ID, models.ImageTypeEnumShotPortrait)...) + + source, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{sourceImage.ID}, + }) + assert.NoError(s.t, err) + sourceID := source.UUID() + s.assign(sourceID, labels(sourceImage.ID, models.ImageTypeEnumShotCandid)...) + + // The form prefill is the union mechanism, so the input names both images + // and both sets of labels. + mergeEdit, err := s.createTestPerformerEdit( + models.OperationEnumMerge, + &models.PerformerEditDetailsInput{ + ImageIds: []uuid.UUID{targetImage.ID, sourceImage.ID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: targetImage.ID, Types: []models.ImageTypeEnum{models.ImageTypeEnumShotPortrait}}, + {ImageID: sourceImage.ID, Types: []models.ImageTypeEnum{models.ImageTypeEnumShotCandid}}, + }, + }, + &models.EditInput{ + Operation: models.OperationEnumMerge, + ID: &targetID, + MergeSourceIds: []uuid.UUID{sourceID}, + }, + nil, + ) + assert.NoError(s.t, err) + + _, err = s.approveEdit(mergeEdit.ID) + assert.NoError(s.t, err) + + after := s.typesOf(targetID) + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumShotPortrait}, after[targetImage.ID]) + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumShotCandid}, after[sourceImage.ID], + "the source's labels should land on the target") +} + +// The edit card reads image_changes, so the flat tuples have to regroup into +// one entry per image or a reviewer gets forty loose changes to correlate. +func (s *performerEditImageTypeTestRunner) testImageChangesGroupByImage() { + first, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + second, err := s.createTestImage(600, 400) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{first.ID, second.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + s.assign(performerID, labels(first.ID, models.ImageTypeEnumCropWide)...) + + // One image gains two labels, loses one and gains a date; the other only + // gains a label. + date := "2019-06" + edit, err := s.createTestPerformerEdit( + models.OperationEnumModify, + &models.PerformerEditDetailsInput{ + ImageIds: []uuid.UUID{first.ID, second.ID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: first.ID, Types: []models.ImageTypeEnum{ + models.ImageTypeEnumShotPortrait, + models.ImageTypeEnumCropFace, + }, Date: &date}, + {ImageID: second.ID, Types: []models.ImageTypeEnum{ + models.ImageTypeEnumShotCandid, + }}, + }, + }, + &models.EditInput{Operation: models.OperationEnumModify, ID: &performerID}, + nil, + ) + assert.NoError(s.t, err) + + data, err := edit.GetPerformerData() + assert.NoError(s.t, err) + + changes, err := s.resolver.PerformerEdit().ImageChanges(s.ctx, data.New) + assert.NoError(s.t, err) + + byImage := make(map[uuid.UUID]models.ImageAssignmentChange, len(changes)) + for _, change := range changes { + byImage[change.Image.ID] = change + } + + assert.Len(s.t, changes, 2, "one entry per affected image") + + assert.ElementsMatch(s.t, []models.ImageTypeEnum{ + models.ImageTypeEnumShotPortrait, + models.ImageTypeEnumCropFace, + }, byImage[first.ID].AddedTypes) + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumCropWide}, + byImage[first.ID].RemovedTypes) + assert.True(s.t, byImage[first.ID].DateChanged) + if assert.NotNil(s.t, byImage[first.ID].Date) { + assert.Equal(s.t, date, *byImage[first.ID].Date) + } + + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumShotCandid}, + byImage[second.ID].AddedTypes) + assert.Empty(s.t, byImage[second.ID].RemovedTypes) + assert.False(s.t, byImage[second.ID].DateChanged, + "an image whose date the edit does not touch must not look changed") +} + +func TestImageChangesGroupByImage(t *testing.T) { + s := createPerformerEditImageTypeTestRunner(t) + s.testImageChangesGroupByImage() +} + +func TestEditAddsImageWithLabels(t *testing.T) { + s := createPerformerEditImageTypeTestRunner(t) + s.testEditAddsImageWithLabels() +} + +func TestEditRetagsExistingImage(t *testing.T) { + s := createPerformerEditImageTypeTestRunner(t) + s.testEditRetagsExistingImage() +} + +func TestEditRemovingImageDropsItsLabels(t *testing.T) { + s := createPerformerEditImageTypeTestRunner(t) + s.testEditRemovingImageDropsItsLabels() +} + +func TestEditSubmittedBeforeLabelsExisted(t *testing.T) { + s := createPerformerEditImageTypeTestRunner(t) + s.testEditSubmittedBeforeLabelsExisted() +} + +func TestPendingEditProtectsImage(t *testing.T) { + s := createPerformerEditImageTypeTestRunner(t) + s.testPendingEditProtectsImage() +} + +func TestConcurrentLabellingMerges(t *testing.T) { + s := createPerformerEditImageTypeTestRunner(t) + s.testConcurrentLabellingMerges() +} + +func TestMergeCarriesSubmittedLabels(t *testing.T) { + s := createPerformerEditImageTypeTestRunner(t) + s.testMergeCarriesSubmittedLabels() +} + +// A newly added image had no date on this performer, so an entry saying null +// is not a date being taken away. The form restates every image's date on +// every save, so this is what an ordinary "add an image" edit looks like -- +// and it was reporting "Date cleared" against a picture that never had one. +func (s *performerEditImageTypeTestRunner) testAddingAnImageWithNoDateIsNotADateChange() { + existing, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{existing.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + added, err := s.createTestImage(600, 900) + assert.NoError(s.t, err) + + dated, err := s.createTestImage(500, 700) + assert.NoError(s.t, err) + date := "2021-03" + + edit, err := s.createTestPerformerEdit( + models.OperationEnumModify, + &models.PerformerEditDetailsInput{ + ImageIds: []uuid.UUID{existing.ID, added.ID, dated.ID}, + ImageTypes: []models.ImageAssignmentInput{ + // Arrives labelled and undated, which is the reported case. + {ImageID: added.ID, Types: []models.ImageTypeEnum{ + models.ImageTypeEnumShotPortrait, + }}, + // Arrives dated. That is a change worth stating. + {ImageID: dated.ID, Types: nil, Date: &date}, + }, + }, + &models.EditInput{Operation: models.OperationEnumModify, ID: &performerID}, + nil, + ) + assert.NoError(s.t, err) + + data, err := edit.GetPerformerData() + assert.NoError(s.t, err) + + changes, err := s.resolver.PerformerEdit().ImageChanges(s.ctx, data.New) + assert.NoError(s.t, err) + + byImage := make(map[uuid.UUID]models.ImageAssignmentChange, len(changes)) + for _, change := range changes { + byImage[change.Image.ID] = change + } + + // Not recorded in the payload at all, which is the fix at its source: the + // resolver guard below is what keeps edits already stored this way reading + // correctly. + for _, date := range data.New.ImageDates { + assert.NotEqual(s.t, added.ID, date.ImageID, + "an undated new image should not be recorded as a date change") + } + + newImage, listed := byImage[added.ID] + if assert.True(s.t, listed, "the added image should still be listed for its label") { + assert.False(s.t, newImage.DateChanged, + "a new image with no date had no date to clear") + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumShotPortrait}, + newImage.AddedTypes) + } + + withDate, listed := byImage[dated.ID] + if assert.True(s.t, listed, "an added image that arrives dated is a date change") { + assert.True(s.t, withDate.DateChanged) + if assert.NotNil(s.t, withDate.Date) { + assert.Equal(s.t, date, *withDate.Date) + } + } +} + +func TestAddingAnImageWithNoDateIsNotADateChange(t *testing.T) { + s := createPerformerEditImageTypeTestRunner(t) + s.testAddingAnImageWithNoDateIsNotADateChange() +} + +// The diff lightbox shows the state being voted on rather than the delta, so +// the edit has to be able to say what each image ends up carrying. +func (s *performerEditImageTypeTestRunner) testTypedImagesAreTheResultingGallery() { + kept, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + dropped, err := s.createTestImage(300, 300) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{kept.ID, dropped.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + s.assign(performerID, labels(kept.ID, models.ImageTypeEnumCropWide)...) + + added, err := s.createTestImage(600, 900) + assert.NoError(s.t, err) + date := "2022" + + edit, err := s.createTestPerformerEdit( + models.OperationEnumModify, + &models.PerformerEditDetailsInput{ + // dropped is left out, so the edit removes it. + ImageIds: []uuid.UUID{kept.ID, added.ID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: kept.ID, Types: []models.ImageTypeEnum{ + models.ImageTypeEnumCropFace, + }, Date: &date}, + {ImageID: added.ID, Types: []models.ImageTypeEnum{ + models.ImageTypeEnumShotPortrait, + }}, + }, + }, + &models.EditInput{Operation: models.OperationEnumModify, ID: &performerID}, + nil, + ) + assert.NoError(s.t, err) + + // Through Details rather than GetPerformerData: EditID is json:"-", so the + // payload comes back without it and everything keyed on the edit -- images, + // urls, aliases, this -- resolves to nothing. Details is where it is put + // back, so going through it is what proves the field is reachable. + details, err := s.resolver.Edit().Details(s.ctx, edit) + assert.NoError(s.t, err) + performerEdit, ok := details.(*models.PerformerEdit) + if !assert.True(s.t, ok, "expected performer edit details") { + return + } + + typed, err := s.resolver.PerformerEdit().TypedImages(s.ctx, performerEdit) + assert.NoError(s.t, err) + + byImage := make(map[uuid.UUID]models.TypedImage, len(typed)) + for _, entry := range typed { + byImage[entry.Image.ID] = entry + } + + assert.Len(s.t, typed, 2, "the gallery the edit results in, not the changes") + assert.NotContains(s.t, byImage, dropped.ID, "a removed image is not in the result") + + // Replaced rather than added to: an entry states the whole of what is true + // about its image. + if entry, ok := byImage[kept.ID]; assert.True(s.t, ok) { + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumCropFace}, entry.Types) + if assert.NotNil(s.t, entry.Date) { + assert.Equal(s.t, date, *entry.Date) + } + } + if entry, ok := byImage[added.ID]; assert.True(s.t, ok) { + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumShotPortrait}, entry.Types) + assert.Nil(s.t, entry.Date) + } +} + +func TestTypedImagesAreTheResultingGallery(t *testing.T) { + s := createPerformerEditImageTypeTestRunner(t) + s.testTypedImagesAreTheResultingGallery() +} + +// The edit path grandfathers the same way the direct one does, and for the same +// reason: an edit restates the labels an image already has. +func (s *performerEditImageTypeTestRunner) testEditKeepsDisabledTypeAlreadyAssigned() { + performerID, imageID := s.createLabelledPerformer(models.ImageTypeEnumShotCandid) + + fresh, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + admin := asAdmin(s.t) + defer func() { + _, _ = admin.resolver.Mutation().ImageTypeSetEnabled(admin.ctx, models.ImageTypeEnabledInput{}) + }() + + _, err = admin.resolver.Mutation().ImageTypeSetEnabled(admin.ctx, models.ImageTypeEnabledInput{ + DisabledTypes: []models.ImageTypeEnum{models.ImageTypeEnumShotCandid}, + }) + assert.NoError(s.t, err) + + // Restating the labels alone is no change at all, so the case that matters + // is an edit to something else that carries them along, the way the form + // submits. + renamed := s.generatePerformerName() + s.applyPerformerEdit(performerID, &models.PerformerEditDetailsInput{ + Name: &renamed, + ImageIds: []uuid.UUID{imageID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: imageID, Types: []models.ImageTypeEnum{models.ImageTypeEnumShotCandid}}, + }, + }) + + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumShotCandid}, + s.typesOf(performerID)[imageID]) + + // Still per image: an edit cannot use the grandfathered label to put the + // switched-off type somewhere it has never been. The resolver is called + // directly because createTestPerformerEdit asserts the edit was created. + _, err = s.resolver.Mutation().PerformerEdit(s.ctx, models.PerformerEditInput{ + Edit: &models.EditInput{Operation: models.OperationEnumModify, ID: &performerID}, + Details: &models.PerformerEditDetailsInput{ + ImageIds: []uuid.UUID{imageID, fresh.ID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: imageID, Types: []models.ImageTypeEnum{models.ImageTypeEnumShotCandid}}, + {ImageID: fresh.ID, Types: []models.ImageTypeEnum{models.ImageTypeEnumShotCandid}}, + }, + }, + }) + assert.ErrorContains(s.t, err, "not enabled on this instance") +} + +func TestEditKeepsDisabledTypeAlreadyAssigned(t *testing.T) { + s := createPerformerEditImageTypeTestRunner(t) + s.testEditKeepsDisabledTypeAlreadyAssigned() +} + +// Merging is what makes two individually valid edits produce an invalid image. +// Each is resolved against current state at apply time, so E1 adding VIEW_FRONT +// and E2 adding VIEW_SIDE both pass validation when they are created -- against +// a clean image -- and contradict each other only once both have landed. POSE +// is exclusive, and nothing in the schema enforces that. +func (s *performerEditImageTypeTestRunner) testConcurrentLabellingCannotContradict() { + performerID, imageID := s.createLabelledPerformer() + + submit := func(imageType models.ImageTypeEnum) uuid.UUID { + s.t.Helper() + edit, err := s.createTestPerformerEdit( + models.OperationEnumModify, + &models.PerformerEditDetailsInput{ + ImageIds: []uuid.UUID{imageID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: imageID, Types: []models.ImageTypeEnum{imageType}}, + }, + }, + &models.EditInput{Operation: models.OperationEnumModify, ID: &performerID}, + nil, + ) + assert.NoError(s.t, err) + return edit.ID + } + + // Both submitted against the same clean image, before either is approved. + front := submit(models.ImageTypeEnumViewFront) + side := submit(models.ImageTypeEnumViewSide) + + applied, err := s.approveEdit(front) + assert.NoError(s.t, err) + assert.Equal(s.t, models.VoteStatusEnumImmediateAccepted.String(), applied.Status) + + // The second now merges into a state its author never saw. A refused apply + // is recorded on the edit rather than returned: the vote already happened, + // and the reviewer needs to see why it did not land. + refused, err := s.approveEdit(side) + assert.NoError(s.t, err) + assert.Equal(s.t, models.VoteStatusEnumFailed.String(), refused.Status) + + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumViewFront}, + s.typesOf(performerID)[imageID], "the refused edit must not have applied") +} + +func TestConcurrentLabellingCannotContradict(t *testing.T) { + s := createPerformerEditImageTypeTestRunner(t) + s.testConcurrentLabellingCannotContradict() +} + +// The same again across groups rather than within one. Exclusivity and +// conflicts_with are separate rules reached by separate code, and only the +// second can say that a face crop cannot be topless. +func (s *performerEditImageTypeTestRunner) testConcurrentLabellingCannotConflict() { + performerID, imageID := s.createLabelledPerformer() + + submit := func(imageType models.ImageTypeEnum) uuid.UUID { + s.t.Helper() + edit, err := s.createTestPerformerEdit( + models.OperationEnumModify, + &models.PerformerEditDetailsInput{ + ImageIds: []uuid.UUID{imageID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: imageID, Types: []models.ImageTypeEnum{imageType}}, + }, + }, + &models.EditInput{Operation: models.OperationEnumModify, ID: &performerID}, + nil, + ) + assert.NoError(s.t, err) + return edit.ID + } + + face := submit(models.ImageTypeEnumCropFace) + topless := submit(models.ImageTypeEnumDressTopless) + + _, err := s.approveEdit(face) + assert.NoError(s.t, err) + + refused, err := s.approveEdit(topless) + assert.NoError(s.t, err) + assert.Equal(s.t, models.VoteStatusEnumFailed.String(), refused.Status) + + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumCropFace}, + s.typesOf(performerID)[imageID]) +} + +func TestConcurrentLabellingCannotConflict(t *testing.T) { + s := createPerformerEditImageTypeTestRunner(t) + s.testConcurrentLabellingCannotConflict() +} diff --git a/internal/api/performer_image_date_integration_test.go b/internal/api/performer_image_date_integration_test.go new file mode 100644 index 000000000..a9c602ae1 --- /dev/null +++ b/internal/api/performer_image_date_integration_test.go @@ -0,0 +1,335 @@ +//go:build integration + +package api_test + +import ( + "testing" + + "github.com/gofrs/uuid" + "github.com/stashapp/stash-box/internal/models" + "github.com/stretchr/testify/assert" +) + +type performerImageDateTestRunner struct { + performerImageTypeTestRunner +} + +func createPerformerImageDateTestRunner(t *testing.T) *performerImageDateTestRunner { + return &performerImageDateTestRunner{ + performerImageTypeTestRunner: *createPerformerImageTypeTestRunner(t), + } +} + +func (s *performerImageDateTestRunner) datesOf(performerID uuid.UUID) map[uuid.UUID]*string { + s.t.Helper() + + s.newRequest() + performer, err := s.resolver.Query().FindPerformer(s.ctx, performerID) + assert.NoError(s.t, err) + + typedImages, err := s.resolver.Performer().TypedImages(s.ctx, performer) + assert.NoError(s.t, err) + + byImage := make(map[uuid.UUID]*string, len(typedImages)) + for _, typedImage := range typedImages { + byImage[typedImage.Image.ID] = typedImage.Date + } + return byImage +} + +func (s *performerImageDateTestRunner) testImageDateFormats() { + image, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{image.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + ctx := s.updateContext([]string{"image_ids", "image_types"}) + update := func(date string) error { + _, err := s.resolver.Mutation().PerformerUpdate(ctx, models.PerformerUpdateInput{ + ID: performerID, + ImageIds: []uuid.UUID{image.ID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: image.ID, Types: []models.ImageTypeEnum{}, Date: &date}, + }, + }) + return err + } + + for _, accepted := range []string{"2019", "2019-06", "2019-06-15"} { + assert.NoError(s.t, update(accepted), "%s should be accepted", accepted) + assert.Equal(s.t, accepted, *s.datesOf(performerID)[image.ID]) + } + + // The column is text, so nothing downstream would catch these. + for _, rejected := range []string{"19-6-1", "2019-13", "2019-06-32", "2019-1", "sometime in 2019", "2019/06/15", ""} { + assert.ErrorContains(s.t, update(rejected), "invalid date", "%q should be rejected", rejected) + } + + // Still the last accepted value: a rejected update changes nothing. + assert.Equal(s.t, "2019-06-15", *s.datesOf(performerID)[image.ID]) +} + +// A date needs no labels to go with it, and needs no image change either. +func (s *performerImageDateTestRunner) testDateOnlyEditOnUnlabelledImage() { + image, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{image.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + date := "2021-03" + edit, err := s.createTestPerformerEdit( + models.OperationEnumModify, + &models.PerformerEditDetailsInput{ + ImageIds: []uuid.UUID{image.ID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: image.ID, Types: []models.ImageTypeEnum{}, Date: &date}, + }, + }, + &models.EditInput{Operation: models.OperationEnumModify, ID: &performerID}, + nil, + ) + assert.NoError(s.t, err) + + _, err = s.approveEdit(edit.ID) + assert.NoError(s.t, err) + + assert.Equal(s.t, date, *s.datesOf(performerID)[image.ID]) + assert.Empty(s.t, s.typesOf(performerID)[image.ID], "the image is dated but still unlabelled") +} + +// The regression this design came closest to shipping. updateImagesFromEdit +// truncates performer_images, and date is a column on those rows, so a +// missing resolve query drops every date whenever an unrelated field changes. +func (s *performerImageDateTestRunner) testDatesSurviveNameOnlyEdit() { + var images []uuid.UUID + for range 3 { + image, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + images = append(images, image.ID) + } + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: images, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + dates := []string{"2019", "2020-06", "2021-03-04"} + assignments := make([]models.ImageAssignmentInput, len(images)) + for i, imageID := range images { + assignments[i] = models.ImageAssignmentInput{ + ImageID: imageID, + Types: []models.ImageTypeEnum{}, + Date: &dates[i], + } + } + + ctx := s.updateContext([]string{"image_ids", "image_types"}) + _, err = s.resolver.Mutation().PerformerUpdate(ctx, models.PerformerUpdateInput{ + ID: performerID, + ImageIds: images, + ImageTypes: assignments, + }) + assert.NoError(s.t, err) + + assertDates := func(context string) { + after := s.datesOf(performerID) + for i, imageID := range images { + if assert.NotNil(s.t, after[imageID], "%s: image %d lost its date", context, i) { + assert.Equal(s.t, dates[i], *after[imageID], context) + } + } + } + assertDates("after dating") + + // An edit that never mentions images. + newName := s.generatePerformerName() + edit, err := s.createTestPerformerEdit( + models.OperationEnumModify, + &models.PerformerEditDetailsInput{Name: &newName}, + &models.EditInput{Operation: models.OperationEnumModify, ID: &performerID}, + nil, + ) + assert.NoError(s.t, err) + + _, err = s.approveEdit(edit.ID) + assert.NoError(s.t, err) + assertDates("after a name-only edit") + + // And the direct path, which rebuilds the same rows. + imageOnlyCtx := s.updateContext([]string{"image_ids"}) + _, err = s.resolver.Mutation().PerformerUpdate(imageOnlyCtx, models.PerformerUpdateInput{ + ID: performerID, + ImageIds: images, + }) + assert.NoError(s.t, err) + assertDates("after a performerUpdate omitting image_types") +} + +func (s *performerImageDateTestRunner) testConcurrentDatingMerges() { + first, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + second, err := s.createTestImage(600, 400) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{first.ID, second.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + bothImages := []uuid.UUID{first.ID, second.ID} + firstDate := "2018" + secondDate := "2022-09" + + dateEdit := func(imageID uuid.UUID, date *string) *models.Edit { + edit, err := s.createTestPerformerEdit( + models.OperationEnumModify, + &models.PerformerEditDetailsInput{ + ImageIds: bothImages, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: imageID, Types: []models.ImageTypeEnum{}, Date: date}, + }, + }, + &models.EditInput{Operation: models.OperationEnumModify, ID: &performerID}, + nil, + ) + assert.NoError(s.t, err) + return edit + } + + editA := dateEdit(first.ID, &firstDate) + editB := dateEdit(second.ID, &secondDate) + + _, err = s.approveEdit(editA.ID) + assert.NoError(s.t, err) + _, err = s.approveEdit(editB.ID) + assert.NoError(s.t, err) + + after := s.datesOf(performerID) + if assert.NotNil(s.t, after[first.ID], "the second edit should keep the first's date") { + assert.Equal(s.t, firstDate, *after[first.ID]) + } + if assert.NotNil(s.t, after[second.ID]) { + assert.Equal(s.t, secondDate, *after[second.ID]) + } +} + +// An entry overrides rather than merges, so a null clears the date. +func (s *performerImageDateTestRunner) testNullClearsDate() { + image, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{image.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + date := "2017-08" + ctx := s.updateContext([]string{"image_ids", "image_types"}) + _, err = s.resolver.Mutation().PerformerUpdate(ctx, models.PerformerUpdateInput{ + ID: performerID, + ImageIds: []uuid.UUID{image.ID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: image.ID, Types: []models.ImageTypeEnum{}, Date: &date}, + }, + }) + assert.NoError(s.t, err) + assert.Equal(s.t, date, *s.datesOf(performerID)[image.ID]) + + _, err = s.resolver.Mutation().PerformerUpdate(ctx, models.PerformerUpdateInput{ + ID: performerID, + ImageIds: []uuid.UUID{image.ID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: image.ID, Types: []models.ImageTypeEnum{}, Date: nil}, + }, + }) + assert.NoError(s.t, err) + assert.Nil(s.t, s.datesOf(performerID)[image.ID], "a null date clears it") +} + +func TestImageDateFormats(t *testing.T) { + s := createPerformerImageDateTestRunner(t) + s.testImageDateFormats() +} + +func TestDateOnlyEditOnUnlabelledImage(t *testing.T) { + s := createPerformerImageDateTestRunner(t) + s.testDateOnlyEditOnUnlabelledImage() +} + +func TestDatesSurviveNameOnlyEdit(t *testing.T) { + s := createPerformerImageDateTestRunner(t) + s.testDatesSurviveNameOnlyEdit() +} + +func TestConcurrentDatingMerges(t *testing.T) { + s := createPerformerImageDateTestRunner(t) + s.testConcurrentDatingMerges() +} + +func TestNullClearsDate(t *testing.T) { + s := createPerformerImageDateTestRunner(t) + s.testNullClearsDate() +} + +// Equally-labelled images come back most recent first, through the real +// resolver rather than the sort in isolation: what the ordering rule is worth +// depends on the dates reaching it, and that is three dataloaders away. +func (s *performerImageDateTestRunner) testGalleryOrdersTiesByDateNewestFirst() { + older, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + newer, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + undated, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{older.ID, newer.ID, undated.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + // The same label on all three, so nothing but the date can separate them. + oldDate, newDate := "2019-06", "2023" + _, err = s.resolver.Mutation().PerformerUpdate(s.ctx, models.PerformerUpdateInput{ + ID: performerID, + ImageIds: []uuid.UUID{older.ID, newer.ID, undated.ID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: older.ID, Types: []models.ImageTypeEnum{models.ImageTypeEnumShotPortrait}, Date: &oldDate}, + {ImageID: newer.ID, Types: []models.ImageTypeEnum{models.ImageTypeEnumShotPortrait}, Date: &newDate}, + {ImageID: undated.ID, Types: []models.ImageTypeEnum{models.ImageTypeEnumShotPortrait}}, + }, + }) + assert.NoError(s.t, err) + + images, err := s.resolver.Performer().Images(s.ctx, &models.Performer{ID: performerID}) + assert.NoError(s.t, err) + + if assert.Len(s.t, images, 3) { + assert.Equal(s.t, newer.ID, images[0].ID, "the most recent goes first") + assert.Equal(s.t, older.ID, images[1].ID) + assert.Equal(s.t, undated.ID, images[2].ID, + "an undated image goes last: no date is not a claim to be old") + } +} + +func TestGalleryOrdersTiesByDateNewestFirst(t *testing.T) { + s := createPerformerImageDateTestRunner(t) + s.testGalleryOrdersTiesByDateNewestFirst() +} diff --git a/internal/api/performer_image_ranking_integration_test.go b/internal/api/performer_image_ranking_integration_test.go new file mode 100644 index 000000000..661ddf963 --- /dev/null +++ b/internal/api/performer_image_ranking_integration_test.go @@ -0,0 +1,212 @@ +//go:build integration + +package api_test + +import ( + "testing" + + "github.com/gofrs/uuid" + "github.com/stashapp/stash-box/internal/models" + "github.com/stretchr/testify/assert" +) + +type performerRankingTestRunner struct { + performerImageTypeTestRunner +} + +func createPerformerRankingTestRunner(t *testing.T) *performerRankingTestRunner { + return &performerRankingTestRunner{ + performerImageTypeTestRunner: *createPerformerImageTypeTestRunner(t), + } +} + +func (s *performerRankingTestRunner) readGroupsForRanking() []models.ImageTypeGroup { + s.t.Helper() + groups, err := s.resolver.Query().ImageTypeGroups(s.ctx, nil, nil) + assert.NoError(s.t, err) + return groups +} + +// Ordering is instance-wide state, so a test that changes it puts it back. +func (s *performerRankingTestRunner) restoreRankingOrder(groups []models.ImageTypeGroup) { + _, err := s.resolver.Mutation().ImageTypeOrderUpdate(s.ctx, orderInputFor(groups)) + assert.NoError(s.t, err) +} + +func (s *performerRankingTestRunner) orderedImages(performerID uuid.UUID) []uuid.UUID { + s.t.Helper() + + s.newRequest() + performer, err := s.resolver.Query().FindPerformer(s.ctx, performerID) + assert.NoError(s.t, err) + + images, err := s.resolver.Performer().Images(s.ctx, performer) + assert.NoError(s.t, err) + + ids := make([]uuid.UUID, len(images)) + for i, image := range images { + ids[i] = image.ID + } + return ids +} + +// The worked example from the design, built as a gallery and read back. +func (s *performerRankingTestRunner) testShippedDefaultOrder() { + // Identical dimensions throughout, so the aspect-ratio tiebreak cannot + // account for the ordering and only the rank tuple can. + newImage := func() uuid.UUID { + image, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + return image.ID + } + + imageA, imageB, imageC, imageD, imageE := newImage(), newImage(), newImage(), newImage(), newImage() + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{imageA, imageB, imageC, imageD, imageE}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + s.assign(performerID, + // A: portrait, face, front, nude -> (0, 0, 0, 3) + models.ImageTypeAssignment{ImageID: imageA, Type: models.ImageTypeEnumShotPortrait}, + models.ImageTypeAssignment{ImageID: imageA, Type: models.ImageTypeEnumCropFace}, + models.ImageTypeAssignment{ImageID: imageA, Type: models.ImageTypeEnumViewFront}, + models.ImageTypeAssignment{ImageID: imageA, Type: models.ImageTypeEnumDressNude}, + // B: portrait, bust, front, non-nude -> (0, 1, 0, 0) + models.ImageTypeAssignment{ImageID: imageB, Type: models.ImageTypeEnumShotPortrait}, + models.ImageTypeAssignment{ImageID: imageB, Type: models.ImageTypeEnumCropBust}, + models.ImageTypeAssignment{ImageID: imageB, Type: models.ImageTypeEnumViewFront}, + models.ImageTypeAssignment{ImageID: imageB, Type: models.ImageTypeEnumDressNonNude}, + // C: portrait, face, front, non-nude -> (0, 0, 0, 0), the primary + models.ImageTypeAssignment{ImageID: imageC, Type: models.ImageTypeEnumShotPortrait}, + models.ImageTypeAssignment{ImageID: imageC, Type: models.ImageTypeEnumCropFace}, + models.ImageTypeAssignment{ImageID: imageC, Type: models.ImageTypeEnumViewFront}, + models.ImageTypeAssignment{ImageID: imageC, Type: models.ImageTypeEnumDressNonNude}, + // E: a tattoo close-up -- detail, face -> (2, 0, inf, inf) + models.ImageTypeAssignment{ImageID: imageE, Type: models.ImageTypeEnumShotDetail}, + models.ImageTypeAssignment{ImageID: imageE, Type: models.ImageTypeEnumCropFace}, + // D is left untyped -> (inf, inf, inf, inf) + ) + + assert.Equal(s.t, []uuid.UUID{imageC, imageA, imageB, imageE, imageD}, s.orderedImages(performerID)) + + // Shot type outranks Crop, so a perfectly cropped tattoo close-up cannot + // take the primary slot from a portrait that is not itself a face crop. + // This is the ordering doing real work. + assert.Equal(s.t, imageC, s.orderedImages(performerID)[0]) + assert.NotEqual(s.t, imageE, s.orderedImages(performerID)[0]) +} + +// An admin who wants nude primaries reorders State of dress. +func (s *performerRankingTestRunner) testAdminReorderChangesPrimary() { + nude, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + clothed, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{nude.ID, clothed.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + // Identical but for state of dress, so that dimension alone decides. + s.assign(performerID, + models.ImageTypeAssignment{ImageID: nude.ID, Type: models.ImageTypeEnumShotPortrait}, + models.ImageTypeAssignment{ImageID: nude.ID, Type: models.ImageTypeEnumCropFace}, + models.ImageTypeAssignment{ImageID: nude.ID, Type: models.ImageTypeEnumDressNude}, + models.ImageTypeAssignment{ImageID: clothed.ID, Type: models.ImageTypeEnumShotPortrait}, + models.ImageTypeAssignment{ImageID: clothed.ID, Type: models.ImageTypeEnumCropFace}, + models.ImageTypeAssignment{ImageID: clothed.ID, Type: models.ImageTypeEnumDressNonNude}, + ) + + assert.Equal(s.t, clothed.ID, s.orderedImages(performerID)[0], "non-nude leads by default") + + before := s.readGroupsForRanking() + defer s.restoreRankingOrder(before) + + // Move DRESS_NUDE ahead of DRESS_NON_NUDE, leaving everything else alone. + reordered := orderInputFor(before) + for i, key := range reordered.Types { + if key == models.ImageTypeEnumDressNonNude { + reordered.Types = append(reordered.Types[:i], reordered.Types[i+1:]...) + reordered.Types = append(reordered.Types, models.ImageTypeEnumDressNonNude) + break + } + } + + _, err = s.resolver.Mutation().ImageTypeOrderUpdate(s.ctx, reordered) + assert.NoError(s.t, err) + + assert.Equal(s.t, nude.ID, s.orderedImages(performerID)[0], + "reordering State of dress should change the primary image") +} + +// The day-one corpus is entirely untyped, and must be unaffected. +func (s *performerRankingTestRunner) testUntypedPerformerUnchanged() { + wide, err := s.createTestImage(900, 300) + assert.NoError(s.t, err) + portrait, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + nearPortrait, err := s.createTestImage(410, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{wide.ID, portrait.ID, nearPortrait.ID}, + }) + assert.NoError(s.t, err) + + // Exactly what the aspect-ratio comparator alone produces: closest to 2:3 + // first, widest last. + assert.Equal(s.t, []uuid.UUID{portrait.ID, nearPortrait.ID, wide.ID}, + s.orderedImages(performer.UUID())) +} + +// Equally ranked images fall back to the comparator rather than to whatever +// order the database returned. +func (s *performerRankingTestRunner) testEqualRanksUseTiebreak() { + wide, err := s.createTestImage(900, 300) + assert.NoError(s.t, err) + portrait, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{wide.ID, portrait.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + s.assign(performerID, + models.ImageTypeAssignment{ImageID: wide.ID, Type: models.ImageTypeEnumShotPortrait}, + models.ImageTypeAssignment{ImageID: portrait.ID, Type: models.ImageTypeEnumShotPortrait}, + ) + + assert.Equal(s.t, []uuid.UUID{portrait.ID, wide.ID}, s.orderedImages(performerID), + "identical tuples should leave the aspect-ratio order intact") +} + +func TestShippedDefaultImageOrder(t *testing.T) { + s := createPerformerRankingTestRunner(t) + s.testShippedDefaultOrder() +} + +func TestAdminReorderChangesPrimary(t *testing.T) { + s := createPerformerRankingTestRunner(t) + s.testAdminReorderChangesPrimary() +} + +func TestUntypedPerformerUnchanged(t *testing.T) { + s := createPerformerRankingTestRunner(t) + s.testUntypedPerformerUnchanged() +} + +func TestEqualRanksUseTiebreak(t *testing.T) { + s := createPerformerRankingTestRunner(t) + s.testEqualRanksUseTiebreak() +} diff --git a/internal/api/performer_image_type_integration_test.go b/internal/api/performer_image_type_integration_test.go new file mode 100644 index 000000000..5b656d388 --- /dev/null +++ b/internal/api/performer_image_type_integration_test.go @@ -0,0 +1,570 @@ +//go:build integration + +package api_test + +import ( + "testing" + + "github.com/gofrs/uuid" + dbtest "github.com/stashapp/stash-box/internal/database/testutil" + "github.com/stashapp/stash-box/internal/models" + "github.com/stretchr/testify/assert" +) + +type performerImageTypeTestRunner struct { + testRunner +} + +func createPerformerImageTypeTestRunner(t *testing.T) *performerImageTypeTestRunner { + return &performerImageTypeTestRunner{ + testRunner: *asAdmin(t), + } +} + +// createLabelledPerformer makes a performer carrying one uploaded image with +// the given labels. +func (s *performerImageTypeTestRunner) createLabelledPerformer(types ...models.ImageTypeEnum) (uuid.UUID, uuid.UUID) { + s.t.Helper() + + image, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{image.ID}, + }) + assert.NoError(s.t, err) + + s.assign(performer.UUID(), labels(image.ID, types...)...) + + return performer.UUID(), image.ID +} + +// assign replaces the performer's assignments wholesale, so every image the +// test cares about has to be named in one call. +func (s *performerImageTypeTestRunner) assign(performerID uuid.UUID, assignments ...models.ImageTypeAssignment) { + s.t.Helper() + + err := dbtest.Factory().ImageType().SetPerformerAssignments(s.ctx, performerID, assignments) + assert.NoError(s.t, err) +} + +func labels(imageID uuid.UUID, types ...models.ImageTypeEnum) []models.ImageTypeAssignment { + assignments := make([]models.ImageTypeAssignment, len(types)) + for i, imageType := range types { + assignments[i] = models.ImageTypeAssignment{ImageID: imageID, Type: imageType} + } + return assignments +} + +func (s *performerImageTypeTestRunner) typesOf(performerID uuid.UUID) map[uuid.UUID][]models.ImageTypeEnum { + s.t.Helper() + + s.newRequest() + performer, err := s.resolver.Query().FindPerformer(s.ctx, performerID) + assert.NoError(s.t, err) + + typedImages, err := s.resolver.Performer().TypedImages(s.ctx, performer) + assert.NoError(s.t, err) + + byImage := make(map[uuid.UUID][]models.ImageTypeEnum, len(typedImages)) + for _, typedImage := range typedImages { + byImage[typedImage.Image.ID] = typedImage.Types + } + return byImage +} + +// The point of task 4: an edit that says nothing about images must not destroy +// their labels. updateImagesFromEdit truncates performer_images on every +// applied edit, and the assignments cascade with it. +func (s *performerImageTypeTestRunner) testAssignmentsSurviveNameOnlyEdit() { + performerID, imageID := s.createLabelledPerformer( + models.ImageTypeEnumShotPortrait, models.ImageTypeEnumCropFace) + + assert.Equal(s.t, []models.ImageTypeEnum{ + models.ImageTypeEnumShotPortrait, + models.ImageTypeEnumCropFace, + }, s.typesOf(performerID)[imageID]) + + newName := s.generatePerformerName() + edit, err := s.createTestPerformerEdit( + models.OperationEnumModify, + &models.PerformerEditDetailsInput{Name: &newName}, + &models.EditInput{Operation: models.OperationEnumModify, ID: &performerID}, + nil, + ) + assert.NoError(s.t, err) + + _, err = s.approveEdit(edit.ID) + assert.NoError(s.t, err) + + assert.Equal(s.t, []models.ImageTypeEnum{ + models.ImageTypeEnumShotPortrait, + models.ImageTypeEnumCropFace, + }, s.typesOf(performerID)[imageID], "labels should survive an edit that never mentions images") +} + +// Removal is scoped by the composite foreign key: one performer losing an +// image must not disturb another performer's labels on the same image. +func (s *performerImageTypeTestRunner) testRemovingImageDropsOnlyThatPerformersAssignments() { + keeperID, imageID := s.createLabelledPerformer(models.ImageTypeEnumCropBust) + + // A second performer carrying the same image, labelled differently. + loser, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{imageID}, + }) + assert.NoError(s.t, err) + loserID := loser.UUID() + s.assign(loserID, labels(imageID, models.ImageTypeEnumDressNude)...) + + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumDressNude}, s.typesOf(loserID)[imageID]) + + ctx := s.updateContext([]string{"image_ids"}) + _, err = s.resolver.Mutation().PerformerUpdate(ctx, models.PerformerUpdateInput{ + ID: loserID, + ImageIds: []uuid.UUID{}, + }) + assert.NoError(s.t, err) + + assert.Empty(s.t, s.typesOf(loserID), "the image and its labels should be gone") + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumCropBust}, s.typesOf(keeperID)[imageID], + "the other performer's labels for the same image should be untouched") + + // Re-adding is what makes an orphaned assignment observable. Every read + // joins through performer_images, so an assignment that outlived its join + // row is invisible until the image comes back -- and then the old labels + // silently resurrect. Asserting emptiness above would pass either way. + _, err = s.resolver.Mutation().PerformerUpdate(ctx, models.PerformerUpdateInput{ + ID: loserID, + ImageIds: []uuid.UUID{imageID}, + }) + assert.NoError(s.t, err) + + assert.Empty(s.t, s.typesOf(loserID)[imageID], "removed labels must not resurrect when the image is re-added") +} + +func (s *performerImageTypeTestRunner) testUnlabelledImageIsStillTyped() { + image, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{image.ID}, + }) + assert.NoError(s.t, err) + + // Untyped is a permanent, valid state: the image still appears, with no + // labels, rather than being omitted. + byImage := s.typesOf(performer.UUID()) + assert.Len(s.t, byImage, 1) + assert.Empty(s.t, byImage[image.ID]) +} + +func (s *performerImageTypeTestRunner) testDirectWriteAcceptsOnePerGroup() { + image, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + // One from every group, and a combination that is actually possible: a + // three-quarter crop rather than a face, because a collarbone-up frame + // cannot establish an uncovered chest and the conflict rules reject it. + oneEach := []models.ImageTypeEnum{ + models.ImageTypeEnumShotPortrait, + models.ImageTypeEnumCropThreeQuarter, + models.ImageTypeEnumViewFront, + models.ImageTypeEnumPostureStanding, + models.ImageTypeEnumDressNude, + } + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{image.ID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: image.ID, Types: oneEach}, + }, + }) + assert.NoError(s.t, err) + + assert.ElementsMatch(s.t, oneEach, s.typesOf(performer.UUID())[image.ID]) +} + +// Enforced on the direct mutation, not only through an edit. The edit path +// looking covered is exactly why this one gets skipped. +func (s *performerImageTypeTestRunner) testDirectWriteRejectsGroupConflict() { + image, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{image.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + ctx := s.updateContext([]string{"image_ids", "image_types"}) + + _, err = s.resolver.Mutation().PerformerUpdate(ctx, models.PerformerUpdateInput{ + ID: performerID, + ImageIds: []uuid.UUID{image.ID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: image.ID, Types: []models.ImageTypeEnum{ + models.ImageTypeEnumCropFace, + models.ImageTypeEnumCropWide, + }}, + }, + }) + assert.ErrorContains(s.t, err, "CROP allows at most one") + + // Labelling an image the performer does not have is refused: the composite + // foreign key would reject it anyway, but with a constraint violation + // rather than something an editor can act on. + stranger, err := uuid.NewV7() + assert.NoError(s.t, err) + + _, err = s.resolver.Mutation().PerformerUpdate(ctx, models.PerformerUpdateInput{ + ID: performerID, + ImageIds: []uuid.UUID{image.ID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: stranger, Types: []models.ImageTypeEnum{models.ImageTypeEnumCropFace}}, + }, + }) + assert.ErrorContains(s.t, err, "not one of this entity's images") +} + +// Cross-group impossibilities. Group exclusivity above catches a group +// contradicting itself; this catches one group contradicting another. +func (s *performerImageTypeTestRunner) testDirectWriteRejectsImpossibleCombination() { + image, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{image.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + ctx := s.updateContext([]string{"image_ids", "image_types"}) + + assign := func(types ...models.ImageTypeEnum) error { + _, err := s.resolver.Mutation().PerformerUpdate(ctx, models.PerformerUpdateInput{ + ID: performerID, + ImageIds: []uuid.UUID{image.ID}, + ImageTypes: []models.ImageAssignmentInput{{ImageID: image.ID, Types: types}}, + }) + return err + } + + rejected := []struct { + name string + types []models.ImageTypeEnum + }{ + // A collarbone-up frame cannot show an uncovered chest. + {"face and topless", []models.ImageTypeEnum{ + models.ImageTypeEnumCropFace, models.ImageTypeEnumDressTopless}}, + {"face and nude", []models.ImageTypeEnum{ + models.ImageTypeEnumCropFace, models.ImageTypeEnumDressNude}}, + // Nothing that stops at the hips can make the genital area the focus. + {"face and explicit", []models.ImageTypeEnum{ + models.ImageTypeEnumCropFace, models.ImageTypeEnumDressExplicit}}, + {"bust and explicit", []models.ImageTypeEnum{ + models.ImageTypeEnumCropBust, models.ImageTypeEnumDressExplicit}}, + {"torso and explicit", []models.ImageTypeEnum{ + models.ImageTypeEnumCropTorso, models.ImageTypeEnumDressExplicit}}, + // A face crop is defined by having a face in it. + {"face and back", []models.ImageTypeEnum{ + models.ImageTypeEnumCropFace, models.ImageTypeEnumViewBack}}, + } + + for _, testCase := range rejected { + err := assign(testCase.types...) + assert.ErrorContains(s.t, err, "cannot be both", testCase.name) + } + + // Order must not matter: the pair is seeded one way round and checked + // both, so listing it the other way is rejected just the same. + assert.ErrorContains(s.t, assign( + models.ImageTypeEnumDressExplicit, models.ImageTypeEnumCropTorso, + ), "cannot be both", "reversed") + + accepted := [][]models.ImageTypeEnum{ + // The looser crops carry every state of dress. + {models.ImageTypeEnumCropThreeQuarter, models.ImageTypeEnumDressExplicit}, + {models.ImageTypeEnumCropFullBody, models.ImageTypeEnumDressExplicit}, + // A profile headshot is ordinary; only Back is impossible. + {models.ImageTypeEnumCropFace, models.ImageTypeEnumViewSide}, + // Face and non-nude is the commonest label there is. + {models.ImageTypeEnumCropFace, models.ImageTypeEnumDressNonNude}, + // A bust crop showing an uncovered chest is exactly what Topless is. + {models.ImageTypeEnumCropBust, models.ImageTypeEnumDressTopless}, + // The trap the thumbnail rule exists for must stay legal. + {models.ImageTypeEnumShotDetail, models.ImageTypeEnumCropFace}, + // Deliberately lenient, not an oversight. A crop stopping at the hips + // cannot strictly establish Nude -- in theory such an image is only + // Topless -- but in practice the two are hard to tell apart, and the + // rules only forbid what the frame makes impossible. Asserted so that + // anyone reading the conflict table as incomplete finds the decision + // here before adding the pair. + {models.ImageTypeEnumCropTorso, models.ImageTypeEnumDressNude}, + {models.ImageTypeEnumCropBust, models.ImageTypeEnumDressNude}, + } + + for _, types := range accepted { + assert.NoError(s.t, assign(types...), "%v should be allowed", types) + } +} + +// The distinction that decides whether existing MODIFY-role clients, which +// know nothing of this field, wipe a gallery's labelling on their next call. +func (s *performerImageTypeTestRunner) testDirectWriteAbsentVersusEmpty() { + labelled, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + untouched, err := s.createTestImage(600, 400) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{labelled.ID, untouched.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + s.assign(performerID, + models.ImageTypeAssignment{ImageID: labelled.ID, Type: models.ImageTypeEnumShotCandid}, + models.ImageTypeAssignment{ImageID: untouched.ID, Type: models.ImageTypeEnumCropWide}, + ) + + bothImages := []uuid.UUID{labelled.ID, untouched.ID} + + // Absent: what every client predating this feature sends. + ctx := s.updateContext([]string{"image_ids"}) + _, err = s.resolver.Mutation().PerformerUpdate(ctx, models.PerformerUpdateInput{ + ID: performerID, + ImageIds: bothImages, + }) + assert.NoError(s.t, err) + + after := s.typesOf(performerID) + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumShotCandid}, after[labelled.ID], + "absent image_types must leave assignments alone") + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumCropWide}, after[untouched.ID]) + + // Non-empty, naming only one image: the other keeps what it has. + typedCtx := s.updateContext([]string{"image_ids", "image_types"}) + _, err = s.resolver.Mutation().PerformerUpdate(typedCtx, models.PerformerUpdateInput{ + ID: performerID, + ImageIds: bothImages, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: labelled.ID, Types: []models.ImageTypeEnum{models.ImageTypeEnumShotDetail}}, + }, + }) + assert.NoError(s.t, err) + + after = s.typesOf(performerID) + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumShotDetail}, after[labelled.ID], + "a named image is authoritative") + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumCropWide}, after[untouched.ID], + "an image in image_ids with no image_types entry keeps its labels") + + // An entry with no types clears just that image. + _, err = s.resolver.Mutation().PerformerUpdate(typedCtx, models.PerformerUpdateInput{ + ID: performerID, + ImageIds: bothImages, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: labelled.ID, Types: []models.ImageTypeEnum{}}, + }, + }) + assert.NoError(s.t, err) + + after = s.typesOf(performerID) + assert.Empty(s.t, after[labelled.ID], "an entry with no types clears that image") + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumCropWide}, after[untouched.ID]) + + // An empty list clears everything. + _, err = s.resolver.Mutation().PerformerUpdate(typedCtx, models.PerformerUpdateInput{ + ID: performerID, + ImageIds: bothImages, + ImageTypes: []models.ImageAssignmentInput{}, + }) + assert.NoError(s.t, err) + + after = s.typesOf(performerID) + assert.Empty(s.t, after[labelled.ID]) + assert.Empty(s.t, after[untouched.ID], "an empty image_types clears every assignment") +} + +func TestAssignmentsSurviveNameOnlyEdit(t *testing.T) { + s := createPerformerImageTypeTestRunner(t) + s.testAssignmentsSurviveNameOnlyEdit() +} + +func TestRemovingImageDropsOnlyThatPerformersAssignments(t *testing.T) { + s := createPerformerImageTypeTestRunner(t) + s.testRemovingImageDropsOnlyThatPerformersAssignments() +} + +func TestUnlabelledImageIsStillTyped(t *testing.T) { + s := createPerformerImageTypeTestRunner(t) + s.testUnlabelledImageIsStillTyped() +} + +func TestDirectWriteAcceptsOnePerGroup(t *testing.T) { + s := createPerformerImageTypeTestRunner(t) + s.testDirectWriteAcceptsOnePerGroup() +} + +func TestDirectWriteRejectsGroupConflict(t *testing.T) { + s := createPerformerImageTypeTestRunner(t) + s.testDirectWriteRejectsGroupConflict() +} + +func TestDirectWriteAbsentVersusEmpty(t *testing.T) { + s := createPerformerImageTypeTestRunner(t) + s.testDirectWriteAbsentVersusEmpty() +} + +func TestDirectWriteRejectsImpossibleCombination(t *testing.T) { + s := createPerformerImageTypeTestRunner(t) + s.testDirectWriteRejectsImpossibleCombination() +} + +// Assigning something the instance has switched off is refused rather than +// quietly dropped: a client that has cached the vocabulary should be told. +func (s *performerImageTypeTestRunner) testDirectWriteRejectsDisabledType() { + image, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{image.ID}, + }) + assert.NoError(s.t, err) + + admin := asAdmin(s.t) + defer func() { + _, _ = admin.resolver.Mutation().ImageTypeSetEnabled(admin.ctx, models.ImageTypeEnabledInput{}) + }() + + _, err = admin.resolver.Mutation().ImageTypeSetEnabled(admin.ctx, models.ImageTypeEnabledInput{ + DisabledTypes: []models.ImageTypeEnum{models.ImageTypeEnumShotCandid}, + }) + assert.NoError(s.t, err) + + ctx := s.updateContext([]string{"image_ids", "image_types"}) + _, err = s.resolver.Mutation().PerformerUpdate(ctx, models.PerformerUpdateInput{ + ID: performer.UUID(), + ImageIds: []uuid.UUID{image.ID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: image.ID, Types: []models.ImageTypeEnum{models.ImageTypeEnumShotCandid}}, + }, + }) + assert.ErrorContains(s.t, err, "not enabled on this instance") + + // A type in a disabled group is refused too, without having been listed. + _, err = admin.resolver.Mutation().ImageTypeSetEnabled(admin.ctx, models.ImageTypeEnabledInput{ + DisabledGroups: []models.ImageTypeGroupEnum{models.ImageTypeGroupEnumPosture}, + }) + assert.NoError(s.t, err) + + _, err = s.resolver.Mutation().PerformerUpdate(ctx, models.PerformerUpdateInput{ + ID: performer.UUID(), + ImageIds: []uuid.UUID{image.ID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: image.ID, Types: []models.ImageTypeEnum{models.ImageTypeEnumPostureStanding}}, + }, + }) + assert.ErrorContains(s.t, err, "not enabled on this instance") +} + +func TestDirectWriteRejectsDisabledType(t *testing.T) { + s := createPerformerImageTypeTestRunner(t) + s.testDirectWriteRejectsDisabledType() +} + +// Switching a type off must not strand the performers already carrying it. +// Every save restates the labels an image has, so refusing those outright would +// make the performer unsaveable -- and the label unremovable, because the only +// way to drop it is a save. +func (s *performerImageTypeTestRunner) testDirectWriteKeepsDisabledTypeAlreadyAssigned() { + performerID, imageID := s.createLabelledPerformer(models.ImageTypeEnumShotCandid) + + admin := asAdmin(s.t) + defer func() { + _, _ = admin.resolver.Mutation().ImageTypeSetEnabled(admin.ctx, models.ImageTypeEnabledInput{}) + }() + + _, err := admin.resolver.Mutation().ImageTypeSetEnabled(admin.ctx, models.ImageTypeEnabledInput{ + DisabledTypes: []models.ImageTypeEnum{models.ImageTypeEnumShotCandid}, + }) + assert.NoError(s.t, err) + + // An unrelated field, restating the labels the way the form does. + renamed := s.generatePerformerName() + ctx := s.updateContext([]string{"name", "image_ids", "image_types"}) + updated, err := s.resolver.Mutation().PerformerUpdate(ctx, models.PerformerUpdateInput{ + ID: performerID, + Name: &renamed, + ImageIds: []uuid.UUID{imageID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: imageID, Types: []models.ImageTypeEnum{models.ImageTypeEnumShotCandid}}, + }, + }) + if assert.NoError(s.t, err) { + assert.Equal(s.t, renamed, updated.Name) + } + + // The label survives, so the editor can still see it and drop it. + assert.Equal(s.t, map[uuid.UUID][]models.ImageTypeEnum{ + imageID: {models.ImageTypeEnumShotCandid}, + }, s.typesOf(performerID)) + + // Dropping it is what the grandfathering is for, and it still works. + _, err = s.resolver.Mutation().PerformerUpdate(ctx, models.PerformerUpdateInput{ + ID: performerID, + Name: &renamed, + ImageIds: []uuid.UUID{imageID}, + ImageTypes: []models.ImageAssignmentInput{{ImageID: imageID}}, + }) + assert.NoError(s.t, err) + assert.Empty(s.t, s.typesOf(performerID)[imageID]) +} + +func TestDirectWriteKeepsDisabledTypeAlreadyAssigned(t *testing.T) { + s := createPerformerImageTypeTestRunner(t) + s.testDirectWriteKeepsDisabledTypeAlreadyAssigned() +} + +// Grandfathering is per image, not per instance: keeping a switched-off label +// where it already is says nothing about spreading it somewhere new. +func (s *performerImageTypeTestRunner) testDirectWriteRejectsDisabledTypeOnAnotherImage() { + performerID, imageID := s.createLabelledPerformer(models.ImageTypeEnumShotCandid) + + fresh, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + admin := asAdmin(s.t) + defer func() { + _, _ = admin.resolver.Mutation().ImageTypeSetEnabled(admin.ctx, models.ImageTypeEnabledInput{}) + }() + + _, err = admin.resolver.Mutation().ImageTypeSetEnabled(admin.ctx, models.ImageTypeEnabledInput{ + DisabledTypes: []models.ImageTypeEnum{models.ImageTypeEnumShotCandid}, + }) + assert.NoError(s.t, err) + + ctx := s.updateContext([]string{"image_ids", "image_types"}) + _, err = s.resolver.Mutation().PerformerUpdate(ctx, models.PerformerUpdateInput{ + ID: performerID, + ImageIds: []uuid.UUID{imageID, fresh.ID}, + ImageTypes: []models.ImageAssignmentInput{ + {ImageID: imageID, Types: []models.ImageTypeEnum{models.ImageTypeEnumShotCandid}}, + {ImageID: fresh.ID, Types: []models.ImageTypeEnum{models.ImageTypeEnumShotCandid}}, + }, + }) + assert.ErrorContains(s.t, err, "not enabled on this instance") +} + +func TestDirectWriteRejectsDisabledTypeOnAnotherImage(t *testing.T) { + s := createPerformerImageTypeTestRunner(t) + s.testDirectWriteRejectsDisabledTypeOnAnotherImage() +} diff --git a/internal/api/performer_integration_test.go b/internal/api/performer_integration_test.go index f72558775..e7df8d5f2 100644 --- a/internal/api/performer_integration_test.go +++ b/internal/api/performer_integration_test.go @@ -284,6 +284,32 @@ func (s *performerTestRunner) verifyUpdatedPerformer(input models.PerformerUpdat assert.Equal(s.t, performer.HipSize, input.HipSize) } +func (s *performerTestRunner) testUpdatePerformerDuplicateImages() { + image, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + createdPerformer, err := s.createTestPerformer(nil) + assert.NoError(s.t, err) + + // The same id twice used to write two identical performer_images rows, + // which the composite primary key now forbids. + updateInput := models.PerformerUpdateInput{ + ID: createdPerformer.UUID(), + ImageIds: []uuid.UUID{image.ID, image.ID}, + } + + ctx := s.updateContext([]string{"image_ids"}) + updatedPerformer, err := s.resolver.Mutation().PerformerUpdate(ctx, updateInput) + assert.NoError(s.t, err) + + s.newRequest() + images, err := s.resolver.Performer().Images(s.ctx, updatedPerformer) + assert.NoError(s.t, err) + if assert.Len(s.t, images, 1) { + assert.Equal(s.t, image.ID, images[0].ID) + } +} + func (s *performerTestRunner) testDestroyPerformer() { createdPerformer, err := s.createTestPerformer(nil) assert.NoError(s.t, err) @@ -332,22 +358,31 @@ func (s *performerTestRunner) testQueryPerformers() { assert.True(s.t, result.Count >= 2, "Expected at least 2 performers in count") assert.True(s.t, len(result.Performers) >= 2, "Expected at least 2 performers in results") - // Verify our created performers are in the results - found1 := false - found2 := false - for _, p := range result.Performers { - if p.ID == performer1.ID { - found1 = true - assert.Equal(s.t, name1, p.Name) - } - if p.ID == performer2.ID { - found2 = true - assert.Equal(s.t, name2, p.Name) + // Verify our created performers are in the results. Filtered by name + // rather than scanned out of the first page: the suite creates more than + // PerPage performers, so an unfiltered page 1 need not contain them. + for _, created := range []struct{ id, name string }{ + {performer1.ID, name1}, + {performer2.ID, name2}, + } { + filtered, err := s.client.queryPerformers(models.PerformerQueryInput{ + Page: 1, + PerPage: 25, + Direction: models.SortDirectionEnumAsc, + Sort: models.PerformerSortEnumName, + Name: &created.name, + }) + assert.NoError(s.t, err, "Error querying performers by name") + + found := false + for _, p := range filtered.Performers { + if p.ID == created.id { + found = true + assert.Equal(s.t, created.name, p.Name) + } } + assert.True(s.t, found, "Created performer %s not found in query results", created.name) } - - assert.True(s.t, found1, "Created performer 1 not found in query results") - assert.True(s.t, found2, "Created performer 2 not found in query results") } func (s *performerTestRunner) testQueryPerformersBirthdate() { @@ -505,6 +540,11 @@ func TestUpdatePerformer(t *testing.T) { // TestUpdatePerformerName is removed due to no longer allowing // partial updates +func TestUpdatePerformerDuplicateImages(t *testing.T) { + pt := createPerformerTestRunner(t) + pt.testUpdatePerformerDuplicateImages() +} + func TestDestroyPerformer(t *testing.T) { pt := createPerformerTestRunner(t) pt.testDestroyPerformer() diff --git a/internal/api/performer_thumbnail_integration_test.go b/internal/api/performer_thumbnail_integration_test.go new file mode 100644 index 000000000..b8c23e276 --- /dev/null +++ b/internal/api/performer_thumbnail_integration_test.go @@ -0,0 +1,243 @@ +//go:build integration + +package api_test + +import ( + "testing" + + "github.com/gofrs/uuid" + "github.com/stashapp/stash-box/internal/models" + "github.com/stretchr/testify/assert" +) + +type performerThumbnailTestRunner struct { + performerImageTypeTestRunner +} + +func createPerformerThumbnailTestRunner(t *testing.T) *performerThumbnailTestRunner { + return &performerThumbnailTestRunner{ + performerImageTypeTestRunner: *createPerformerImageTypeTestRunner(t), + } +} + +func (s *performerThumbnailTestRunner) thumbnailFor(viewer *testRunner, performerID uuid.UUID) *models.Image { + s.t.Helper() + + viewer.newRequest() + performer, err := viewer.resolver.Query().FindPerformer(viewer.ctx, performerID) + assert.NoError(s.t, err) + + thumbnail, err := viewer.resolver.Performer().Thumbnail(viewer.ctx, performer) + assert.NoError(s.t, err) + return thumbnail +} + +// The regression test for the prepended-boost mistake. A face-tattoo close-up +// must not beat a portrait that is not itself a face crop: Shot type outranks +// Crop, and overriding the Crop component leaves that intact where prepending +// a boost would not. +func (s *performerThumbnailTestRunner) testDetailFaceLosesToPortrait() { + tattoo, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + portrait, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{tattoo.ID, portrait.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + s.assign(performerID, + // A close-up of a face tattoo: a face crop, but not a photograph of + // the person. + models.ImageTypeAssignment{ImageID: tattoo.ID, Type: models.ImageTypeEnumShotDetail}, + models.ImageTypeAssignment{ImageID: tattoo.ID, Type: models.ImageTypeEnumCropFace}, + // A portrait, cropped less tightly. + models.ImageTypeAssignment{ImageID: portrait.ID, Type: models.ImageTypeEnumShotPortrait}, + models.ImageTypeAssignment{ImageID: portrait.ID, Type: models.ImageTypeEnumCropBust}, + ) + + thumbnail := s.thumbnailFor(asRead(s.t), performerID) + if assert.NotNil(s.t, thumbnail) { + assert.Equal(s.t, portrait.ID, thumbnail.ID, + "a SHOT_DETAIL face crop must not outrank a SHOT_PORTRAIT image") + } +} + +// Among photographs of the person, the face crop wins -- and keeps winning +// when the viewer's preference ranks it last. +func (s *performerThumbnailTestRunner) testFacePreferredDespiteViewerPreference() { + face, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + body, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{face.ID, body.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + s.assign(performerID, + models.ImageTypeAssignment{ImageID: face.ID, Type: models.ImageTypeEnumShotPortrait}, + models.ImageTypeAssignment{ImageID: face.ID, Type: models.ImageTypeEnumCropFace}, + models.ImageTypeAssignment{ImageID: body.ID, Type: models.ImageTypeEnumShotPortrait}, + models.ImageTypeAssignment{ImageID: body.ID, Type: models.ImageTypeEnumCropFullBody}, + ) + + viewer := asEdit(s.t) + assert.Equal(s.t, face.ID, s.thumbnailFor(viewer, performerID).ID) + + // The viewer prefers full body and ranks the face crop last. Their gallery + // changes; their thumbnails do not. + _, err = viewer.resolver.Mutation().UpdateImageTypePreferences(viewer.ctx, models.ImageTypePreferencesInput{Types: []models.ImageTypeEnum{ + models.ImageTypeEnumCropFullBody, + models.ImageTypeEnumCropTorso, + models.ImageTypeEnumCropWide, + models.ImageTypeEnumCropThreeQuarter, + models.ImageTypeEnumCropThreeQuarterPlus, + models.ImageTypeEnumCropBust, + models.ImageTypeEnumCropFace, + }}) + assert.NoError(s.t, err) + defer func() { + _, _ = viewer.resolver.Mutation().UpdateImageTypePreferences(viewer.ctx, clearPreferences()) + }() + + viewer.newRequest() + performerForViewer, err := viewer.resolver.Query().FindPerformer(viewer.ctx, performerID) + assert.NoError(s.t, err) + images, err := viewer.resolver.Performer().Images(viewer.ctx, performerForViewer) + assert.NoError(s.t, err) + assert.Equal(s.t, body.ID, images[0].ID, "the gallery should follow the preference") + + assert.Equal(s.t, face.ID, s.thumbnailFor(viewer, performerID).ID, + "the thumbnail must ignore the viewer's preference") +} + +// With no face crop anywhere, this is just the instance ordering's first +// image -- today's behaviour. +func (s *performerThumbnailTestRunner) testFallsBackToInstanceOrdering() { + bust, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + wide, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{wide.ID, bust.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + s.assign(performerID, + models.ImageTypeAssignment{ImageID: bust.ID, Type: models.ImageTypeEnumShotPortrait}, + models.ImageTypeAssignment{ImageID: bust.ID, Type: models.ImageTypeEnumCropBust}, + models.ImageTypeAssignment{ImageID: wide.ID, Type: models.ImageTypeEnumShotPortrait}, + models.ImageTypeAssignment{ImageID: wide.ID, Type: models.ImageTypeEnumCropWide}, + ) + + viewer := asRead(s.t) + assert.Equal(s.t, bust.ID, s.thumbnailFor(viewer, performerID).ID) + + // And with nothing labelled at all, the aspect-ratio comparator decides. + untypedPortrait, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + untypedWide, err := s.createTestImage(900, 300) + assert.NoError(s.t, err) + + untyped, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{untypedWide.ID, untypedPortrait.ID}, + }) + assert.NoError(s.t, err) + + assert.Equal(s.t, untypedPortrait.ID, s.thumbnailFor(viewer, untyped.UUID()).ID) +} + +// Viewer-independence has to be checked outside the Crop dimension. The +// override replaces the Crop component wholesale, so a preference within Crop +// cannot show whether the thumbnail used the viewer's ordering or the +// instance's -- only the other groups can. +func (s *performerThumbnailTestRunner) testThumbnailIgnoresPreferenceInOtherGroups() { + clothed, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + nude, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{clothed.ID, nude.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + // Both are face crops, so the override ties that component and State of + // dress decides. + s.assign(performerID, + models.ImageTypeAssignment{ImageID: clothed.ID, Type: models.ImageTypeEnumShotPortrait}, + models.ImageTypeAssignment{ImageID: clothed.ID, Type: models.ImageTypeEnumCropFace}, + models.ImageTypeAssignment{ImageID: clothed.ID, Type: models.ImageTypeEnumDressNonNude}, + models.ImageTypeAssignment{ImageID: nude.ID, Type: models.ImageTypeEnumShotPortrait}, + models.ImageTypeAssignment{ImageID: nude.ID, Type: models.ImageTypeEnumCropFace}, + models.ImageTypeAssignment{ImageID: nude.ID, Type: models.ImageTypeEnumDressNude}, + ) + + viewer := asEdit(s.t) + assert.Equal(s.t, clothed.ID, s.thumbnailFor(viewer, performerID).ID) + + _, err = viewer.resolver.Mutation().UpdateImageTypePreferences(viewer.ctx, models.ImageTypePreferencesInput{Types: []models.ImageTypeEnum{ + models.ImageTypeEnumDressNude, + }}) + assert.NoError(s.t, err) + defer func() { + _, _ = viewer.resolver.Mutation().UpdateImageTypePreferences(viewer.ctx, clearPreferences()) + }() + + viewer.newRequest() + performerForViewer, err := viewer.resolver.Query().FindPerformer(viewer.ctx, performerID) + assert.NoError(s.t, err) + images, err := viewer.resolver.Performer().Images(viewer.ctx, performerForViewer) + assert.NoError(s.t, err) + assert.Equal(s.t, nude.ID, images[0].ID, "the gallery should follow the preference") + + assert.Equal(s.t, clothed.ID, s.thumbnailFor(viewer, performerID).ID, + "the thumbnail must rank against the instance ordering, not the viewer's") +} + +func (s *performerThumbnailTestRunner) testNoImagesIsNull() { + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + }) + assert.NoError(s.t, err) + + assert.Nil(s.t, s.thumbnailFor(asRead(s.t), performer.UUID())) +} + +func TestThumbnailDetailFaceLosesToPortrait(t *testing.T) { + s := createPerformerThumbnailTestRunner(t) + s.testDetailFaceLosesToPortrait() +} + +func TestThumbnailFacePreferredDespiteViewerPreference(t *testing.T) { + s := createPerformerThumbnailTestRunner(t) + s.testFacePreferredDespiteViewerPreference() +} + +func TestThumbnailFallsBackToInstanceOrdering(t *testing.T) { + s := createPerformerThumbnailTestRunner(t) + s.testFallsBackToInstanceOrdering() +} + +func TestThumbnailIgnoresPreferenceInOtherGroups(t *testing.T) { + s := createPerformerThumbnailTestRunner(t) + s.testThumbnailIgnoresPreferenceInOtherGroups() +} + +func TestThumbnailNoImagesIsNull(t *testing.T) { + s := createPerformerThumbnailTestRunner(t) + s.testNoImagesIsNull() +} diff --git a/internal/api/resolver.go b/internal/api/resolver.go index 0daa27e70..190631cdc 100644 --- a/internal/api/resolver.go +++ b/internal/api/resolver.go @@ -54,6 +54,9 @@ func (r *Resolver) TagCategory() models.TagCategoryResolver { func (r *Resolver) Image() models.ImageResolver { return &imageResolver{r} } +func (r *Resolver) ImageType() models.ImageTypeResolver { + return &imageTypeResolver{r} +} func (r *Resolver) Studio() models.StudioResolver { return &studioResolver{r} } diff --git a/internal/api/resolver_model_image_type.go b/internal/api/resolver_model_image_type.go new file mode 100644 index 000000000..1e7e52532 --- /dev/null +++ b/internal/api/resolver_model_image_type.go @@ -0,0 +1,102 @@ +package api + +import ( + "context" + + "github.com/stashapp/stash-box/internal/image/croptemplate" + "github.com/stashapp/stash-box/internal/models" +) + +type imageTypeResolver struct{ *Resolver } + +// CropTemplate is the frame to crop to for a type, read from the Photoshop +// template it ships with +// +// A field resolver rather than something carried on the model, so the template +// is parsed only when a client asks for the frame. Most types have no template +// at all - nothing about a pose or a state of dress says anything about the +// shape of the picture +func (r *imageTypeResolver) CropTemplate(ctx context.Context, obj *models.ImageType) (*models.CropTemplate, error) { + template, ok := r.services.CropTemplates().Template(string(obj.Key)) + if !ok { + return nil, nil + } + + guides := make([]models.CropGuide, 0, len(template.Guides)) + for _, guide := range template.Guides { + guides = append(guides, models.CropGuide{ + Axis: models.CropGuideAxisEnum(guide.Axis), + Position: guide.Position, + Role: cropGuideRole(guide.Role), + Label: cropGuideLabel(guide.Label), + Pivot: guide.Pivot, + }) + } + + return &models.CropTemplate{ + AspectRatio: template.AspectRatio(), + Guides: guides, + Shapes: cropShapes(template.Shapes), + }, nil +} + +// cropShapes converts the outlines a template draws on its own layers +// +// A straight copy of the geometry rather than an SVG path built here: the +// client draws one, but a bounding box or a centre is the kind of thing the +// frame will want next, and a string it would have to parse back is a poor way +// to hand it over +func cropShapes(shapes []croptemplate.Shape) []models.CropShape { + out := make([]models.CropShape, 0, len(shapes)) + + for _, shape := range shapes { + subpaths := make([]models.CropSubpath, 0, len(shape.Subpaths)) + for _, subpath := range shape.Subpaths { + knots := make([]models.CropKnot, 0, len(subpath.Knots)) + for _, knot := range subpath.Knots { + knots = append(knots, models.CropKnot{ + ControlIn: cropPoint(knot.In), + Anchor: cropPoint(knot.Anchor), + ControlOut: cropPoint(knot.Out), + }) + } + subpaths = append(subpaths, models.CropSubpath{ + Closed: subpath.Closed, + Knots: knots, + }) + } + + out = append(out, models.CropShape{ + Label: cropGuideLabel(shape.Label), + Subpaths: subpaths, + }) + } + + return out +} + +func cropPoint(p croptemplate.Point) *models.CropPoint { + return &models.CropPoint{X: p.X, Y: p.Y} +} + +// cropGuideRole maps an unroled guide to null rather than to an empty string a +// client would have to know to ignore. A template need not say how closely each +// of its lines is meant to be followed +// +// Empty is the only value needing translation: croptemplate normalises a role +// it does not recognise to empty when it reads the template, so anything else +// arriving here is already one of the known ones +func cropGuideRole(role croptemplate.Role) *models.CropGuideRoleEnum { + if role == "" { + return nil + } + out := models.CropGuideRoleEnum(role) + return &out +} + +func cropGuideLabel(label string) *string { + if label == "" { + return nil + } + return &label +} diff --git a/internal/api/resolver_model_performer.go b/internal/api/resolver_model_performer.go index 96c77d66c..2c554529f 100644 --- a/internal/api/resolver_model_performer.go +++ b/internal/api/resolver_model_performer.go @@ -86,14 +86,152 @@ func (r *performerResolver) Piercings(ctx context.Context, obj *models.Performer return dataloader.For(ctx).PerformerPiercingsByID.Load(obj.ID) } +// gallery is a performer's images with everything that decides their order. +type gallery struct { + images []models.Image + assignments []models.ImageTypeAssignment + // Keyed by image id, and absent for an image nobody has dated. + dates map[uuid.UUID]*string +} + +// loadGallery reads all three. +// +// Together because all three image resolvers need all three, and the +// dataloaders behind them are per-request: asking twice is not a second query, +// it is a second place for them to be paired differently. +func loadGallery(ctx context.Context, performerID uuid.UUID) (gallery, error) { + imageIDs, err := dataloader.For(ctx).PerformerImageIDsByID.Load(performerID) + if err != nil { + return gallery{}, err + } + + images, err := imageList(ctx, imageIDs) + if err != nil { + return gallery{}, err + } + + assignments, err := dataloader.For(ctx).PerformerImageTypesByID.Load(performerID) + if err != nil { + return gallery{}, err + } + + dates, err := dataloader.For(ctx).PerformerImageDatesByID.Load(performerID) + if err != nil { + return gallery{}, err + } + + byImage := make(map[uuid.UUID]*string, len(dates)) + for _, date := range dates { + byImage[date.ImageID] = date.Date + } + + return gallery{images: images, assignments: assignments, dates: byImage}, nil +} + +// performerGallery is that gallery in display order, which is what Images and +// TypedImages both are: one is the pictures, the other the same pictures with +// what they are labelled. +func performerGallery(ctx context.Context, performerID uuid.UUID) (gallery, error) { + g, err := loadGallery(ctx, performerID) + if err != nil { + return gallery{}, err + } + + ranks, err := performerImageRanks(ctx, g.assignments) + if err != nil { + return gallery{}, err + } + + // Rank, then date, then shape. The set never changes, only its order: a + // performer whose images are all untyped and undated comes back exactly as + // OrderPortrait alone would have ordered it. + image.OrderByType(g.images, ranks, image.NewestFirst(g.dates, image.OrderPortrait)) + + return g, nil +} + func (r *performerResolver) Images(ctx context.Context, obj *models.Performer) ([]models.Image, error) { - imageIDs, err := dataloader.For(ctx).PerformerImageIDsByID.Load(obj.ID) + g, err := performerGallery(ctx, obj.ID) + return g.images, err +} + +func (r *performerResolver) Thumbnail(ctx context.Context, obj *models.Performer) (*models.Image, error) { + // Not performerGallery: this orders by a different ranking, so it takes the + // gallery unordered and does its own. + g, err := loadGallery(ctx, obj.ID) if err != nil { return nil, err } - images, err := imageList(ctx, imageIDs) - image.OrderPortrait(images) - return images, err + + if len(g.images) == 0 { + return nil, nil + } + + var ranks map[uuid.UUID]image.RankTuple + if len(g.assignments) > 0 { + vocabulary, err := dataloader.For(ctx).ImageTypeVocabulary.Get() + if err != nil { + return nil, err + } + + // Instance(): deliberately not the viewer's ordering. A face crop is + // easier to recognise at 40px whatever the viewer likes in a gallery, + // and viewer-independence is what makes this field cacheable. + ranks = vocabulary.Instance().ThumbnailRanksByImage(g.assignments) + } + + // Dated before undated here too, so a performer with two equally good face + // crops leads with the newer one -- and the thumbnail stops depending on + // which of them the aspect sort happened to prefer. + image.OrderByType(g.images, ranks, image.NewestFirst(g.dates, image.OrderPortrait)) + + return &g.images[0], nil +} + +// performerImageRanks builds each image's rank tuple against the ordering this +// viewer sees. The dataloader resolves the vocabulary once per request, from +// the viewer's own preference, so a gallery is ordered the way its reader asked +// for. Thumbnail is the one field that opts out. +func performerImageRanks(ctx context.Context, assignments []models.ImageTypeAssignment) (map[uuid.UUID]image.RankTuple, error) { + if len(assignments) == 0 { + // Nothing to rank, and no reason to read the vocabulary. + return nil, nil + } + + vocabulary, err := dataloader.For(ctx).ImageTypeVocabulary.Get() + if err != nil { + return nil, err + } + + return vocabulary.RanksByImage(assignments), nil +} + +func (r *performerResolver) TypedImages(ctx context.Context, obj *models.Performer) ([]models.TypedImage, error) { + // Same order as Images, because it is the same call: the two are views of + // one gallery, and the assignments that ordered it are the ones being + // reported. + g, err := performerGallery(ctx, obj.ID) + if err != nil { + return nil, err + } + + typesByImage := make(map[uuid.UUID][]models.ImageTypeEnum, len(g.assignments)) + for _, assignment := range g.assignments { + typesByImage[assignment.ImageID] = append(typesByImage[assignment.ImageID], assignment.Type) + } + + // One entry per image, labelled or not: an untyped image is a normal and + // permanent state, not an omission. + typedImages := make([]models.TypedImage, len(g.images)) + for i := range g.images { + typedImages[i] = models.TypedImage{ + Image: &g.images[i], + Types: typesByImage[g.images[i].ID], + Date: g.dates[g.images[i].ID], + } + } + + return typedImages, nil } func (r *performerResolver) Edits(ctx context.Context, obj *models.Performer) ([]models.Edit, error) { diff --git a/internal/api/resolver_model_performer_edit.go b/internal/api/resolver_model_performer_edit.go index 35630a5f2..010a24671 100644 --- a/internal/api/resolver_model_performer_edit.go +++ b/internal/api/resolver_model_performer_edit.go @@ -3,6 +3,8 @@ package api import ( "context" + "github.com/gofrs/uuid" + "github.com/stashapp/stash-box/internal/models" "github.com/stashapp/stash-box/pkg/utils" ) @@ -62,10 +64,99 @@ func (r *performerEditResolver) RemovedImages(ctx context.Context, obj *models.P return imageList(ctx, obj.RemovedImages) } +// ImageChanges regroups the edit's flat label tuples and date overrides into +// one entry per affected image, which is the unit a reviewer reads. +func (r *performerEditResolver) ImageChanges(ctx context.Context, obj *models.PerformerEdit) ([]models.ImageAssignmentChange, error) { + // Preserve first-seen order so the list is stable between renders rather + // than following Go's map iteration. + var order []uuid.UUID + changes := make(map[uuid.UUID]*models.ImageAssignmentChange) + + forImage := func(imageID uuid.UUID) *models.ImageAssignmentChange { + change, seen := changes[imageID] + if !seen { + change = &models.ImageAssignmentChange{} + changes[imageID] = change + order = append(order, imageID) + } + return change + } + + for _, added := range obj.AddedImageTypes { + change := forImage(added.ImageID) + change.AddedTypes = append(change.AddedTypes, added.Type) + } + + for _, removed := range obj.RemovedImageTypes { + change := forImage(removed.ImageID) + change.RemovedTypes = append(change.RemovedTypes, removed.Type) + } + + // An image arriving with this edit had no date on this performer to begin + // with, so an entry saying null is not a date being taken away. The form + // restates every image's date on every save, so a newly added image with + // no date produces exactly that entry, and reporting it as a change had + // reviewers reading "Date cleared" against a picture that never had one. + // + // Only the added case is caught, and deliberately. Telling whether a date + // really changed on an image the performer already had would mean + // comparing against the current value, which is right while the edit is + // pending and wrong once it is applied -- the current value is then the + // edit's own. A diff has to read the same before and after applying, and + // nothing records what the date was at submission. + added := make(map[uuid.UUID]struct{}, len(obj.AddedImages)) + for _, imageID := range obj.AddedImages { + added[imageID] = struct{}{} + } + + for _, date := range obj.ImageDates { + if _, isNew := added[date.ImageID]; isNew && date.Date == nil { + continue + } + change := forImage(date.ImageID) + change.DateChanged = true + change.Date = date.Date + } + + if len(order) == 0 { + return nil, nil + } + + images, err := imageList(ctx, order) + if err != nil { + return nil, err + } + + byID := make(map[uuid.UUID]models.Image, len(images)) + for _, image := range images { + byID[image.ID] = image + } + + result := make([]models.ImageAssignmentChange, 0, len(order)) + for _, imageID := range order { + image, found := byID[imageID] + if !found { + // Garbage-collected since the edit was submitted; nothing useful + // to show a reviewer. + continue + } + + change := changes[imageID] + change.Image = &image + result = append(result, *change) + } + + return result, nil +} + func (r *performerEditResolver) Images(ctx context.Context, obj *models.PerformerEdit) ([]models.Image, error) { return r.services.Edit().GetMergedImages(ctx, obj.EditID) } +func (r *performerEditResolver) TypedImages(ctx context.Context, obj *models.PerformerEdit) ([]models.TypedImage, error) { + return r.services.Edit().GetMergedTypedImages(ctx, obj.EditID) +} + func (r *performerEditResolver) Urls(ctx context.Context, obj *models.PerformerEdit) ([]models.URL, error) { return r.services.Edit().GetMergedURLs(ctx, obj.EditID) } diff --git a/internal/api/resolver_model_user.go b/internal/api/resolver_model_user.go index 2ff1d59de..56a10f496 100644 --- a/internal/api/resolver_model_user.go +++ b/internal/api/resolver_model_user.go @@ -76,6 +76,14 @@ func (r *userResolver) InviteCodes(ctx context.Context, user *models.User) ([]mo return r.services.UserToken().FindActiveInviteKeysForUser(ctx, user.ID) } +func (r *userResolver) ImageTypePreferences(ctx context.Context, user *models.User) ([]models.ImageTypeEnum, error) { + return r.services.ImageType().Preferences(ctx, user.ID) +} + +func (r *userResolver) ImageTypeGroupPreferences(ctx context.Context, user *models.User) ([]models.ImageTypeGroupEnum, error) { + return r.services.ImageType().GroupPreferences(ctx, user.ID) +} + func (r *userResolver) NotificationSubscriptions(ctx context.Context, user *models.User) ([]models.NotificationEnum, error) { return r.services.User().GetNotificationSubscriptions(ctx, user.ID) } diff --git a/internal/api/resolver_mutation_image_type.go b/internal/api/resolver_mutation_image_type.go new file mode 100644 index 000000000..4cf66fc86 --- /dev/null +++ b/internal/api/resolver_mutation_image_type.go @@ -0,0 +1,15 @@ +package api + +import ( + "context" + + "github.com/stashapp/stash-box/internal/models" +) + +func (r *mutationResolver) ImageTypeOrderUpdate(ctx context.Context, input models.ImageTypeOrderInput) ([]models.ImageTypeGroup, error) { + return r.services.ImageType().UpdateOrder(ctx, input) +} + +func (r *mutationResolver) ImageTypeSetEnabled(ctx context.Context, input models.ImageTypeEnabledInput) ([]models.ImageTypeGroup, error) { + return r.services.ImageType().SetEnabled(ctx, input) +} diff --git a/internal/api/resolver_mutation_image_type_preferences.go b/internal/api/resolver_mutation_image_type_preferences.go new file mode 100644 index 000000000..0388300f9 --- /dev/null +++ b/internal/api/resolver_mutation_image_type_preferences.go @@ -0,0 +1,23 @@ +package api + +import ( + "context" + + "github.com/stashapp/stash-box/internal/auth" + "github.com/stashapp/stash-box/internal/models" +) + +func (r *mutationResolver) UpdateImageTypePreferences(ctx context.Context, input models.ImageTypePreferencesInput) (bool, error) { + user := auth.GetCurrentUser(ctx) + service := r.services.ImageType() + + // The two lists live in separate tables, because their keys reference + // different vocabularies, but they are one preference to the user and are + // written in one transaction: a failure between them would leave an + // ordering nobody asked for. + if err := service.SetPreferences(ctx, user.ID, input.Types, input.Groups); err != nil { + return false, err + } + + return true, nil +} diff --git a/internal/api/resolver_query_image_type.go b/internal/api/resolver_query_image_type.go new file mode 100644 index 000000000..5e16a7d10 --- /dev/null +++ b/internal/api/resolver_query_image_type.go @@ -0,0 +1,11 @@ +package api + +import ( + "context" + + "github.com/stashapp/stash-box/internal/models" +) + +func (r *queryResolver) ImageTypeGroups(ctx context.Context, target *models.ImageTypeScopeEnum, includeDisabled *bool) ([]models.ImageTypeGroup, error) { + return r.services.ImageType().Groups(ctx, target, includeDisabled != nil && *includeDisabled) +} diff --git a/internal/api/routes_crop_template.go b/internal/api/routes_crop_template.go new file mode 100644 index 000000000..a5bf67d4f --- /dev/null +++ b/internal/api/routes_crop_template.go @@ -0,0 +1,52 @@ +package api + +import ( + "net/http" + "strings" + + "github.com/go-chi/chi/v5" + + "github.com/stashapp/stash-box/internal/image/croptemplate" + "github.com/stashapp/stash-box/pkg/logger" +) + +// Only the loader, not the whole factory: this route touches no database, and +// the narrower dependency is what lets it be tested without one +type cropTemplateRoutes struct { + templates *croptemplate.Loader +} + +func (rs cropTemplateRoutes) Routes() chi.Router { + r := chi.NewRouter() + r.Get("/{key}", rs.template) + return r +} + +// template streams a crop template for someone cropping in their own editor +// +// Served, not generated: these are the bytes the overlay in the edit form was +// parsed from, so the frame a contributor drags here and the one they drag in +// Photoshop are the same +func (rs cropTemplateRoutes) template(w http.ResponseWriter, r *http.Request) { + key := strings.TrimSuffix(chi.URLParam(r, "key"), croptemplate.TemplateExt) + + data, ok := rs.templates.Bytes(key) + if !ok { + http.Error(w, "no such crop template", http.StatusNotFound) + return + } + + // The registered type for Photoshop documents. Browsers do not preview it, + // which is what we want: this is a file to open in an editor + w.Header().Set("Content-Type", "image/vnd.adobe.photoshop") + // The Content-Type above is only worth stating if it is also believed + w.Header().Set("X-Content-Type-Options", "nosniff") + w.Header().Set("Content-Disposition", + "attachment; filename=\""+key+croptemplate.TemplateExt+"\"") + // Templates are baked into the binary, so they only change with a release + w.Header().Set("Cache-Control", "public, max-age=3600") + + if _, err := w.Write(data); err != nil { + logger.Errorf("writing crop template %s: %v", key, err) + } +} diff --git a/internal/api/routes_crop_template_test.go b/internal/api/routes_crop_template_test.go new file mode 100644 index 000000000..916c5c0a7 --- /dev/null +++ b/internal/api/routes_crop_template_test.go @@ -0,0 +1,112 @@ +package api + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stashapp/stash-box/internal/image/croptemplate" +) + +// The route touches no database, so this is a plain test rather than an +// integration one. What it has to establish is the guarantee the whole design +// rests on: the file handed over is the file the overlay was parsed from, so a +// contributor cropping in their own editor and one cropping in the form are +// provably working to the same frame + +func serveWith(t *testing.T, loader *croptemplate.Loader, path string) *httptest.ResponseRecorder { + t.Helper() + + res := httptest.NewRecorder() + cropTemplateRoutes{templates: loader}. + Routes(). + ServeHTTP(res, httptest.NewRequestWithContext(context.Background(), http.MethodGet, path, nil)) + return res +} + +func serve(t *testing.T, path string) *httptest.ResponseRecorder { + t.Helper() + return serveWith(t, croptemplate.NewLoader(), path) +} + +func TestCropTemplateDownloadServesTheFileTheOverlayUses(t *testing.T) { + res := serve(t, "/CROP_FACE") + + if res.Code != http.StatusOK { + t.Fatalf("status %d, want 200", res.Code) + } + + downloaded, err := croptemplate.Parse(res.Body.Bytes()) + if err != nil { + t.Fatalf("the served file does not parse: %v", err) + } + + shown, ok := croptemplate.NewLoader().Template("CROP_FACE") + if !ok { + t.Fatal("no template to compare against") + } + + if downloaded.Width != shown.Width || downloaded.Height != shown.Height { + t.Errorf("download is %dx%d but the overlay is %dx%d", + downloaded.Width, downloaded.Height, shown.Width, shown.Height) + } + if len(downloaded.Guides) != len(shown.Guides) { + t.Fatalf("download has %d guides, the overlay %d", + len(downloaded.Guides), len(shown.Guides)) + } + for i, guide := range downloaded.Guides { + if guide != shown.Guides[i] { + t.Errorf("guide %d differs: %+v vs %+v", i, guide, shown.Guides[i]) + } + } +} + +// Photoshop's registered type, and an attachment: this is a file to open in an +// editor, not something a browser should try to preview +func TestCropTemplateDownloadHeaders(t *testing.T) { + res := serve(t, "/CROP_FACE") + + if got := res.Header().Get("Content-Type"); got != "image/vnd.adobe.photoshop" { + t.Errorf("Content-Type %q", got) + } + if got := res.Header().Get("Content-Disposition"); got != `attachment; filename="CROP_FACE.psd"` { + t.Errorf("Content-Disposition %q", got) + } +} + +// A link ending in .psd reads as a file; one that does not is easier to write. +// Both have to reach the same template +func TestCropTemplateDownloadAcceptsTheExtensionEitherWay(t *testing.T) { + bare := serve(t, "/CROP_WIDE") + suffixed := serve(t, "/CROP_WIDE.psd") + + if bare.Code != http.StatusOK || suffixed.Code != http.StatusOK { + t.Fatalf("statuses %d and %d, want 200", bare.Code, suffixed.Code) + } + if bare.Body.Len() != suffixed.Body.Len() { + t.Error("the two spellings served different files") + } +} + +func TestCropTemplateDownloadRefusesWhatItHasNoTemplateFor(t *testing.T) { + for _, path := range []string{ + "/CROP_NOSUCH", + // A real image type, with no frame of its own: nothing about which way + // the subject faces says anything about the shape of the picture + "/VIEW_FRONT", + } { + t.Run(path, func(t *testing.T) { + if code := serve(t, path).Code; code != http.StatusNotFound { + t.Errorf("status %d, want 404", code) + } + }) + } +} + +// Anonymous, like a request to /images: nothing here needs a role +func TestCropTemplateDownloadNeedsNoAuthentication(t *testing.T) { + if code := serveWith(t, croptemplate.NewLoader(), "/CROP_FACE").Code; code != http.StatusOK { + t.Errorf("status %d for an unauthenticated request, want 200", code) + } +} diff --git a/internal/api/routes_root.go b/internal/api/routes_root.go index 3f9d8490b..644da297b 100644 --- a/internal/api/routes_root.go +++ b/internal/api/routes_root.go @@ -30,6 +30,10 @@ func (rr rootRoutes) Routes(fac service.Factory) chi.Router { fac: fac, }.Routes()) + r.Mount("/crop-templates", cropTemplateRoutes{ + templates: fac.CropTemplates(), + }.Routes()) + // Serve static assets r.HandleFunc("/assets/*", rr.assets) r.HandleFunc("/favicon.ico", rr.assets) diff --git a/internal/api/scene_edit_integration_test.go b/internal/api/scene_edit_integration_test.go index e938d4f20..b2c8cba7a 100644 --- a/internal/api/scene_edit_integration_test.go +++ b/internal/api/scene_edit_integration_test.go @@ -352,8 +352,7 @@ func (s *sceneEditTestRunner) testApplyModifyUnsetSceneEdit() { func (s *sceneEditTestRunner) testApplyDestroySceneEdit() { // Create a scene with an image and a fingerprint - imgURL := "http://example.org/image.jpg" - image, err := s.resolver.Mutation().ImageCreate(s.ctx, models.ImageCreateInput{URL: &imgURL}) + image, err := s.createTestImage(600, 400) assert.NoError(s.t, err) sceneInput := &models.SceneCreateInput{ diff --git a/internal/api/user_image_type_preference_integration_test.go b/internal/api/user_image_type_preference_integration_test.go new file mode 100644 index 000000000..dcf205cb0 --- /dev/null +++ b/internal/api/user_image_type_preference_integration_test.go @@ -0,0 +1,429 @@ +//go:build integration + +package api_test + +import ( + "testing" + + "github.com/gofrs/uuid" + "github.com/stashapp/stash-box/internal/models" + "github.com/stretchr/testify/assert" +) + +type imageTypePreferenceTestRunner struct { + performerImageTypeTestRunner +} + +func createImageTypePreferenceTestRunner(t *testing.T) *imageTypePreferenceTestRunner { + return &imageTypePreferenceTestRunner{ + performerImageTypeTestRunner: *createPerformerImageTypeTestRunner(t), + } +} + +// orderedFor reads the gallery as one particular viewer sees it. +func (s *imageTypePreferenceTestRunner) orderedFor(viewer *testRunner, performerID uuid.UUID) []uuid.UUID { + s.t.Helper() + + viewer.newRequest() + performer, err := viewer.resolver.Query().FindPerformer(viewer.ctx, performerID) + assert.NoError(s.t, err) + + images, err := viewer.resolver.Performer().Images(viewer.ctx, performer) + assert.NoError(s.t, err) + + ids := make([]uuid.UUID, len(images)) + for i, image := range images { + ids[i] = image.ID + } + return ids +} + +// A gallery of three images differing only in state of dress, so that +// dimension alone decides the order. +func (s *imageTypePreferenceTestRunner) dressGallery() (uuid.UUID, map[models.ImageTypeEnum]uuid.UUID) { + s.t.Helper() + + dressTypes := []models.ImageTypeEnum{ + models.ImageTypeEnumDressNonNude, + models.ImageTypeEnumDressTopless, + models.ImageTypeEnumDressNude, + } + + byType := make(map[models.ImageTypeEnum]uuid.UUID, len(dressTypes)) + var imageIDs []uuid.UUID + for _, dressType := range dressTypes { + image, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + byType[dressType] = image.ID + imageIDs = append(imageIDs, image.ID) + } + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: imageIDs, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + var assignments []models.ImageTypeAssignment + for _, dressType := range dressTypes { + assignments = append(assignments, + models.ImageTypeAssignment{ImageID: byType[dressType], Type: models.ImageTypeEnumShotPortrait}, + models.ImageTypeAssignment{ImageID: byType[dressType], Type: models.ImageTypeEnumCropFace}, + models.ImageTypeAssignment{ImageID: byType[dressType], Type: dressType}, + ) + } + s.assign(performerID, assignments...) + + return performerID, byType +} + +func (s *imageTypePreferenceTestRunner) testPreferenceReordersForOwnerOnly() { + performerID, byType := s.dressGallery() + + instanceOrder := []uuid.UUID{ + byType[models.ImageTypeEnumDressNonNude], + byType[models.ImageTypeEnumDressTopless], + byType[models.ImageTypeEnumDressNude], + } + + owner := asEdit(s.t) + bystander := asModify(s.t) + + assert.Equal(s.t, instanceOrder, s.orderedFor(owner, performerID)) + assert.Equal(s.t, instanceOrder, s.orderedFor(bystander, performerID)) + + // The owner prefers topless first, then nude. + applied, err := owner.resolver.Mutation().UpdateImageTypePreferences(owner.ctx, models.ImageTypePreferencesInput{Types: []models.ImageTypeEnum{ + models.ImageTypeEnumDressTopless, + models.ImageTypeEnumDressNude, + }}) + assert.NoError(s.t, err) + assert.True(s.t, applied) + + assert.Equal(s.t, []uuid.UUID{ + byType[models.ImageTypeEnumDressTopless], + byType[models.ImageTypeEnumDressNude], + // Unlisted, so it trails the listed ones in instance order. + byType[models.ImageTypeEnumDressNonNude], + }, s.orderedFor(owner, performerID), "a partial preference is well defined") + + // Dataloaders are built per request, so one viewer's ordering cannot leak + // into another's even in the same process. + assert.Equal(s.t, instanceOrder, s.orderedFor(bystander, performerID), + "another user's ordering must be untouched") + + // And it round-trips through the user field. + me, err := owner.resolver.Query().Me(owner.ctx) + assert.NoError(s.t, err) + preferences, err := owner.resolver.User().ImageTypePreferences(owner.ctx, me) + assert.NoError(s.t, err) + assert.Equal(s.t, []models.ImageTypeEnum{ + models.ImageTypeEnumDressTopless, + models.ImageTypeEnumDressNude, + }, preferences) + + // An empty list clears it, returning the owner to the instance ordering. + _, err = owner.resolver.Mutation().UpdateImageTypePreferences(owner.ctx, clearPreferences()) + assert.NoError(s.t, err) + + assert.Equal(s.t, instanceOrder, s.orderedFor(owner, performerID), "an empty list clears the preference") + + preferences, err = owner.resolver.User().ImageTypePreferences(owner.ctx, me) + assert.NoError(s.t, err) + assert.Empty(s.t, preferences) +} + +// A type preference reorders within a dimension and nothing more: promoting a +// crop cannot lift a detail shot past a portrait, because that is decided by +// Shot type. Reordering the dimensions needs a group preference. +func (s *imageTypePreferenceTestRunner) testPreferenceCannotOutrankGroups() { + detail, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + portrait, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{detail.ID, portrait.ID}, + }) + assert.NoError(s.t, err) + performerID := performer.UUID() + + s.assign(performerID, + // A tattoo close-up that is also a face crop. + models.ImageTypeAssignment{ImageID: detail.ID, Type: models.ImageTypeEnumShotDetail}, + models.ImageTypeAssignment{ImageID: detail.ID, Type: models.ImageTypeEnumCropFace}, + // A portrait cropped less tightly. + models.ImageTypeAssignment{ImageID: portrait.ID, Type: models.ImageTypeEnumShotPortrait}, + models.ImageTypeAssignment{ImageID: portrait.ID, Type: models.ImageTypeEnumCropBust}, + ) + + viewer := asEdit(s.t) + + // Even preferring face crops above everything, Shot type outranks Crop and + // the preference cannot promote a detail shot past a portrait. + _, err = viewer.resolver.Mutation().UpdateImageTypePreferences(viewer.ctx, models.ImageTypePreferencesInput{Types: []models.ImageTypeEnum{ + models.ImageTypeEnumCropFace, + }}) + assert.NoError(s.t, err) + defer func() { + _, _ = viewer.resolver.Mutation().UpdateImageTypePreferences(viewer.ctx, clearPreferences()) + }() + + assert.Equal(s.t, []uuid.UUID{portrait.ID, detail.ID}, s.orderedFor(viewer, performerID), + "a type preference must not reorder groups") +} + +// A gallery where Crop and State of dress disagree about which image leads, so +// whichever dimension is compared first decides. +func (s *imageTypePreferenceTestRunner) disagreeingGallery() (uuid.UUID, uuid.UUID, uuid.UUID) { + s.t.Helper() + + faceNude, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + wideClothed, err := s.createTestImage(400, 600) + assert.NoError(s.t, err) + + performer, err := s.createTestPerformer(&models.PerformerCreateInput{ + Name: s.generatePerformerName(), + ImageIds: []uuid.UUID{faceNude.ID, wideClothed.ID}, + }) + assert.NoError(s.t, err) + + s.assign(performer.UUID(), + // Wins on Crop, loses on State of dress. + models.ImageTypeAssignment{ImageID: faceNude.ID, Type: models.ImageTypeEnumShotPortrait}, + models.ImageTypeAssignment{ImageID: faceNude.ID, Type: models.ImageTypeEnumCropFace}, + models.ImageTypeAssignment{ImageID: faceNude.ID, Type: models.ImageTypeEnumDressNude}, + // And the reverse. + models.ImageTypeAssignment{ImageID: wideClothed.ID, Type: models.ImageTypeEnumShotPortrait}, + models.ImageTypeAssignment{ImageID: wideClothed.ID, Type: models.ImageTypeEnumCropWide}, + models.ImageTypeAssignment{ImageID: wideClothed.ID, Type: models.ImageTypeEnumDressNonNude}, + ) + + return performer.UUID(), faceNude.ID, wideClothed.ID +} + +// Promoting a dimension is what a type preference alone cannot do: it decides +// which comparison happens first rather than breaking ties inside one. +func (s *imageTypePreferenceTestRunner) testGroupPreferenceReordersDimensions() { + performerID, faceNude, wideClothed := s.disagreeingGallery() + + owner := asEdit(s.t) + bystander := asModify(s.t) + + // Crop is compared before State of dress, so the face crop leads. + assert.Equal(s.t, []uuid.UUID{faceNude, wideClothed}, s.orderedFor(owner, performerID)) + + _, err := owner.resolver.Mutation().UpdateImageTypePreferences(owner.ctx, models.ImageTypePreferencesInput{ + Groups: []models.ImageTypeGroupEnum{models.ImageTypeGroupEnumDress}, + }) + assert.NoError(s.t, err) + defer func() { + _, _ = owner.resolver.Mutation().UpdateImageTypePreferences(owner.ctx, clearPreferences()) + }() + + assert.Equal(s.t, []uuid.UUID{wideClothed, faceNude}, s.orderedFor(owner, performerID), + "promoting State of dress should decide the order before Crop does") + + assert.Equal(s.t, []uuid.UUID{faceNude, wideClothed}, s.orderedFor(bystander, performerID), + "one user's group order must not reach anybody else") +} + +// The thumbnail rule ranks against the instance ordering, so no amount of +// reordering by the viewer changes what other people's search results show. +// Only a group preference can test this properly: the Crop override ties that +// dimension, so a Crop preference could never have revealed which ordering was +// used. +func (s *imageTypePreferenceTestRunner) testGroupPreferenceLeavesThumbnailAlone() { + performerID, faceNude, wideClothed := s.disagreeingGallery() + + viewer := asEdit(s.t) + + // Both preferences at once, which is what a user who has touched the screen + // at all will have. The two are applied in layers, and the layering is + // where Instance() can be lost: whichever is applied second must still + // reach past the first to the unadjusted ordering. + _, err := viewer.resolver.Mutation().UpdateImageTypePreferences(viewer.ctx, models.ImageTypePreferencesInput{ + Groups: []models.ImageTypeGroupEnum{models.ImageTypeGroupEnumDress}, + Types: []models.ImageTypeEnum{models.ImageTypeEnumCropWide}, + }) + assert.NoError(s.t, err) + defer func() { + _, _ = viewer.resolver.Mutation().UpdateImageTypePreferences(viewer.ctx, clearPreferences()) + }() + + assert.Equal(s.t, []uuid.UUID{wideClothed, faceNude}, s.orderedFor(viewer, performerID), + "the gallery should follow the preference") + + viewer.newRequest() + performer, err := viewer.resolver.Query().FindPerformer(viewer.ctx, performerID) + assert.NoError(s.t, err) + thumbnail, err := viewer.resolver.Performer().Thumbnail(viewer.ctx, performer) + assert.NoError(s.t, err) + + if assert.NotNil(s.t, thumbnail) { + assert.Equal(s.t, faceNude, thumbnail.ID, + "the thumbnail must ignore the viewer's group order") + } +} + +// Groups left out trail the ones named, in instance order -- the same rule +// that applies to types, so a user need not rank all four to say one thing. +func (s *imageTypePreferenceTestRunner) testPartialGroupPreference() { + owner := asEdit(s.t) + + _, err := owner.resolver.Mutation().UpdateImageTypePreferences(owner.ctx, models.ImageTypePreferencesInput{ + Groups: []models.ImageTypeGroupEnum{models.ImageTypeGroupEnumDress}, + }) + assert.NoError(s.t, err) + defer func() { + _, _ = owner.resolver.Mutation().UpdateImageTypePreferences(owner.ctx, clearPreferences()) + }() + + owner.newRequest() + me, err := owner.resolver.Query().Me(owner.ctx) + assert.NoError(s.t, err) + + stored, err := owner.resolver.User().ImageTypeGroupPreferences(owner.ctx, me) + assert.NoError(s.t, err) + assert.Equal(s.t, []models.ImageTypeGroupEnum{models.ImageTypeGroupEnumDress}, stored, + "only what the user named is stored") + + // Clearing returns them to the instance ordering. + _, err = owner.resolver.Mutation().UpdateImageTypePreferences(owner.ctx, clearPreferences()) + assert.NoError(s.t, err) + + owner.newRequest() + me, err = owner.resolver.Query().Me(owner.ctx) + assert.NoError(s.t, err) + stored, err = owner.resolver.User().ImageTypeGroupPreferences(owner.ctx, me) + assert.NoError(s.t, err) + assert.Empty(s.t, stored) +} + +func TestGroupPreferenceReordersDimensions(t *testing.T) { + s := createImageTypePreferenceTestRunner(t) + s.testGroupPreferenceReordersDimensions() +} + +func TestGroupPreferenceLeavesThumbnailAlone(t *testing.T) { + s := createImageTypePreferenceTestRunner(t) + s.testGroupPreferenceLeavesThumbnailAlone() +} + +func TestPartialGroupPreference(t *testing.T) { + s := createImageTypePreferenceTestRunner(t) + s.testPartialGroupPreference() +} + +func TestPreferenceReordersForOwnerOnly(t *testing.T) { + s := createImageTypePreferenceTestRunner(t) + s.testPreferenceReordersForOwnerOnly() +} + +func TestPreferenceCannotOutrankGroups(t *testing.T) { + s := createImageTypePreferenceTestRunner(t) + s.testPreferenceCannotOutrankGroups() +} + +// A disabled dimension stops deciding anything. The gallery in +// disagreeingGallery is ordered by Crop; switching Crop off should hand the +// decision to State of dress, the next enabled group. +func (s *imageTypePreferenceTestRunner) testDisabledGroupDropsOutOfRanking() { + performerID, faceNude, wideClothed := s.disagreeingGallery() + + admin := asAdmin(s.t) + defer func() { + _, _ = admin.resolver.Mutation().ImageTypeSetEnabled(admin.ctx, models.ImageTypeEnabledInput{}) + }() + + viewer := asRead(s.t) + assert.Equal(s.t, []uuid.UUID{faceNude, wideClothed}, s.orderedFor(viewer, performerID), + "Crop decides while it is in use") + + _, err := admin.resolver.Mutation().ImageTypeSetEnabled(admin.ctx, models.ImageTypeEnabledInput{ + DisabledGroups: []models.ImageTypeGroupEnum{models.ImageTypeGroupEnumCrop}, + }) + assert.NoError(s.t, err) + + assert.Equal(s.t, []uuid.UUID{wideClothed, faceNude}, s.orderedFor(viewer, performerID), + "with Crop off, State of dress decides instead") + + // One type off, its group still in use: Crop keeps deciding, but the image + // whose only crop label was the disabled one now has nothing to be ranked + // on and falls behind. Asserted separately from the group case because a + // disabled group would hide the type anyway -- this is the only way to see + // the type's own flag doing the work. + _, err = admin.resolver.Mutation().ImageTypeSetEnabled(admin.ctx, models.ImageTypeEnabledInput{ + DisabledTypes: []models.ImageTypeEnum{models.ImageTypeEnumCropFace}, + }) + assert.NoError(s.t, err) + + assert.Equal(s.t, []uuid.UUID{wideClothed, faceNude}, s.orderedFor(viewer, performerID), + "an image loses its place when the type it was ranked on is switched off") + + // And the labels are still there: switching it back on restores the + // original order rather than leaving the gallery permanently rearranged. + _, err = admin.resolver.Mutation().ImageTypeSetEnabled(admin.ctx, models.ImageTypeEnabledInput{}) + assert.NoError(s.t, err) + + assert.Equal(s.t, []uuid.UUID{faceNude, wideClothed}, s.orderedFor(viewer, performerID), + "re-enabling is lossless") +} + +func TestDisabledGroupDropsOutOfRanking(t *testing.T) { + s := createImageTypePreferenceTestRunner(t) + s.testDisabledGroupDropsOutOfRanking() +} + +// clearPreferences states both lists as empty. Absent and empty are different +// answers for groups -- absent leaves whatever the user had -- so a test that +// means "clear" has to say so. +func clearPreferences() models.ImageTypePreferencesInput { + return models.ImageTypePreferencesInput{ + Types: []models.ImageTypeEnum{}, + Groups: []models.ImageTypeGroupEnum{}, + } +} + +// The group list is not defaulted, so a client updating only its type +// order threw away a group order it had never mentioned. Absent now means what +// it says. +func (s *imageTypePreferenceTestRunner) testTypeUpdateKeepsGroupPreference() { + owner := asRead(s.t) + defer func() { + _, _ = owner.resolver.Mutation().UpdateImageTypePreferences(owner.ctx, clearPreferences()) + }() + + _, err := owner.resolver.Mutation().UpdateImageTypePreferences(owner.ctx, models.ImageTypePreferencesInput{ + Types: []models.ImageTypeEnum{}, + Groups: []models.ImageTypeGroupEnum{models.ImageTypeGroupEnumDress}, + }) + assert.NoError(s.t, err) + + // Types only, saying nothing about groups. + _, err = owner.resolver.Mutation().UpdateImageTypePreferences(owner.ctx, models.ImageTypePreferencesInput{ + Types: []models.ImageTypeEnum{models.ImageTypeEnumDressNude}, + }) + assert.NoError(s.t, err) + + owner.newRequest() + me, err := owner.resolver.Query().Me(owner.ctx) + assert.NoError(s.t, err) + + groups, err := owner.resolver.User().ImageTypeGroupPreferences(owner.ctx, me) + assert.NoError(s.t, err) + assert.Equal(s.t, []models.ImageTypeGroupEnum{models.ImageTypeGroupEnumDress}, groups, + "a type-only update must not clear the group preference") + + types, err := owner.resolver.User().ImageTypePreferences(owner.ctx, me) + assert.NoError(s.t, err) + assert.Equal(s.t, []models.ImageTypeEnum{models.ImageTypeEnumDressNude}, types) +} + +func TestTypeUpdateKeepsGroupPreference(t *testing.T) { + s := createImageTypePreferenceTestRunner(t) + s.testTypeUpdateKeepsGroupPreference() +} diff --git a/internal/database/database.go b/internal/database/database.go index f225ee546..74e517908 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -22,7 +22,7 @@ import ( const ( postgresDriver = "postgres" - schemaVersion = 75 + schemaVersion = 81 ) //go:embed migrations/postgres/*.sql diff --git a/internal/database/migrations/postgres/76_performer_images_pkey.up.sql b/internal/database/migrations/postgres/76_performer_images_pkey.up.sql new file mode 100644 index 000000000..d424d69b0 --- /dev/null +++ b/internal/database/migrations/postgres/76_performer_images_pkey.up.sql @@ -0,0 +1,15 @@ +-- Migration 04 created performer_images with bare FK columns and migration 11 +-- only replaced the FKs, so duplicate (performer_id, image_id) rows are possible +-- in deployed databases. Remove them before adding the key; ctid is the only way +-- to distinguish two otherwise identical rows. +DELETE FROM performer_images pi +WHERE pi.ctid <> ( + SELECT min(pi2.ctid) FROM performer_images pi2 + WHERE pi2.performer_id = pi.performer_id AND pi2.image_id = pi.image_id +); + +ALTER TABLE performer_images +ADD CONSTRAINT performer_images_pkey PRIMARY KEY (performer_id, image_id); + +-- Redundant now that the primary key's btree leads with performer_id +DROP INDEX performer_images_performer_id_idx; diff --git a/internal/database/migrations/postgres/77_image_types.up.sql b/internal/database/migrations/postgres/77_image_types.up.sql new file mode 100644 index 000000000..4532db66d --- /dev/null +++ b/internal/database/migrations/postgres/77_image_types.up.sql @@ -0,0 +1,121 @@ +-- The image type vocabulary. Keyed by stable strings rather than generated ids +-- so a key means the same thing on every instance, and so edit payloads stay +-- readable. Rows are seeded here and never created or destroyed at runtime, +-- hence no created_at/updated_at and no soft delete. + +CREATE TABLE image_type_groups ( + "key" text PRIMARY KEY, + "name" text NOT NULL, + "description" text, + -- Dimension priority when ranking images; lower wins. Admin-writable. + "sort_order" integer NOT NULL, + -- At most one type from this group may be assigned to an image. Fixed. + "exclusive" boolean NOT NULL, + -- Admin-writable. A disabled group is not offered when labelling and takes + -- no part in ranking, but its rows and any assignments made while it was on + -- are kept, so switching it back on restores what an instance had. The + -- taxonomy is meant to be complete; an instance is not obliged to use all + -- of it. + "enabled" boolean NOT NULL DEFAULT true, + -- The ranking has no tiebreak below it but the entity's aspect-ratio + -- comparator, so two rows sharing a sort_order would make the order of two + -- differently-typed images arbitrary. Deferred so a full reorder can be one + -- UPDATE per row in a single transaction, without contriving a + -- collision-free intermediate permutation. + CONSTRAINT image_type_groups_sort_order_key UNIQUE ("sort_order") DEFERRABLE INITIALLY DEFERRED +); + +CREATE TABLE image_types ( + "key" text PRIMARY KEY, + "name" text NOT NULL, + "description" text, + "group_key" text NOT NULL REFERENCES image_type_groups("key"), + -- Value priority within the group; lower wins. Admin-writable. + "sort_order" integer NOT NULL, + -- Which entity kinds this type may be applied to. Fixed. Always + -- ['PERFORMER'] today: this is NOT a generalizable cross-entity field like + -- sites.valid_types, which it otherwise resembles down to the containment + -- check. When scenes and studios get image labelling, they get their own + -- separate types and groups, not rows here with SCENE or STUDIO added. + "valid_types" text[] NOT NULL CHECK ("valid_types" <@ ARRAY['SCENE', 'PERFORMER', 'STUDIO']), + -- Admin-writable, as on the group. Disabling a group disables its types by + -- implication rather than by cascade, so the two can be reasoned about + -- separately and re-enabling a group does not resurrect a type that was + -- switched off on its own. + "enabled" boolean NOT NULL DEFAULT true, + CONSTRAINT image_types_group_sort_order_key UNIQUE ("group_key", "sort_order") DEFERRABLE INITIALLY DEFERRED +); + +CREATE INDEX image_types_group_key_idx ON image_types (group_key); + +-- Pairs that cannot both describe one image, across groups. Exclusivity within +-- a group is a property of the group; this is the cross-group case, and every +-- pair here follows one rule: a value requires the crop to include the anatomy +-- it describes. +-- +-- Deliberately confined to the impossible. A combination that is merely +-- unlikely, or that a tight crop happens not to establish, is left to the +-- labeller -- rejecting a correct label is worse than rejecting nothing, and a +-- rule tightened later would strand rows already stored. +-- +-- Stored one way round and checked both ways, so a pair cannot be half-seeded. +CREATE TABLE image_type_conflicts ( + "type_key" text NOT NULL REFERENCES image_types("key"), + "conflicts_with_key" text NOT NULL REFERENCES image_types("key"), + PRIMARY KEY ("type_key", "conflicts_with_key"), + CONSTRAINT image_type_conflicts_not_self CHECK ("type_key" <> "conflicts_with_key") +); + +-- Every group key is a literal prefix of its type keys. That is a rule rather +-- than a convention, and an integration test asserts it. +INSERT INTO image_type_groups ("key", "name", "description", "sort_order", "exclusive") VALUES + ('SHOT', 'Shot type', 'What kind of photograph this is', 0, true), + ('CROP', 'Crop', 'How much of the subject is in frame', 1, true), + ('VIEW', 'View', 'Which side of the subject faces the camera', 2, true), + ('POSTURE', 'Posture', 'What the subject''s body is doing', 3, true), + ('DRESS', 'State of dress', 'How much clothing the subject is wearing', 4, true); + +INSERT INTO image_types ("key", "name", "description", "group_key", "sort_order", "valid_types") VALUES + ('SHOT_PORTRAIT', 'Portrait', 'Posed or promotional', 'SHOT', 0, ARRAY['PERFORMER']), + ('SHOT_CANDID', 'Candid', 'Unposed, amateur or self-shot: event photography, behind-the-scenes, selfies, screenshots', 'SHOT', 1, ARRAY['PERFORMER']), + ('SHOT_DETAIL', 'Detail', 'A close-up of a feature rather than the person: tattoo, piercing, scar', 'SHOT', 2, ARRAY['PERFORMER']), + + ('CROP_FACE', 'Face', 'Collarbone up, the quarter-length headshot', 'CROP', 0, ARRAY['PERFORMER']), + ('CROP_BUST', 'Bust', 'Navel or chest up, the half-length shot', 'CROP', 1, ARRAY['PERFORMER']), + ('CROP_THREE_QUARTER', 'Three-quarter', 'Mid-thigh up, the three-quarter-length shot', 'CROP', 2, ARRAY['PERFORMER']), + ('CROP_THREE_QUARTER_PLUS', 'Three-quarter plus', 'A looser three-quarter, framed on 80-20 thirds', 'CROP', 3, ARRAY['PERFORMER']), + ('CROP_FULL_BODY', 'Full body', 'Head to toe, the full-length shot', 'CROP', 4, ARRAY['PERFORMER']), + ('CROP_TORSO', 'Torso', 'Hips to shoulders, head not necessarily included', 'CROP', 5, ARRAY['PERFORMER']), + ('CROP_WIDE', 'Wide', 'The whole scene, or the subject small in frame', 'CROP', 6, ARRAY['PERFORMER']), + + ('VIEW_FRONT', 'Front', 'Photographed from the front', 'VIEW', 0, ARRAY['PERFORMER']), + ('VIEW_SIDE', 'Side', 'Photographed from the side', 'VIEW', 1, ARRAY['PERFORMER']), + ('VIEW_BACK', 'Back', 'Photographed from behind', 'VIEW', 2, ARRAY['PERFORMER']), + + ('POSTURE_STANDING', 'Standing', 'Upright and on their feet', 'POSTURE', 0, ARRAY['PERFORMER']), + ('POSTURE_SITTING', 'Sitting', 'Seated, on anything or on the ground', 'POSTURE', 1, ARRAY['PERFORMER']), + ('POSTURE_KNEELING', 'Kneeling', 'Weight on the knees, torso upright', 'POSTURE', 2, ARRAY['PERFORMER']), + ('POSTURE_SQUATTING', 'Squatting', 'Crouched on the feet, knees bent and off the ground', 'POSTURE', 3, ARRAY['PERFORMER']), + ('POSTURE_ON_ALL_FOURS', 'On all fours', 'Weight on both hands and knees', 'POSTURE', 4, ARRAY['PERFORMER']), + ('POSTURE_LYING', 'Lying', 'Horizontal: on the front, back or side', 'POSTURE', 5, ARRAY['PERFORMER']), + ('POSTURE_SUSPENDED', 'Suspended', 'Held off the ground, as in bondage photography', 'POSTURE', 6, ARRAY['PERFORMER']), + + ('DRESS_NON_NUDE', 'Non-nude', 'Clothed', 'DRESS', 0, ARRAY['PERFORMER']), + ('DRESS_UNDERWEAR', 'Underwear', 'Underwear, lingerie, swimwear or comparably revealing clothing', 'DRESS', 1, ARRAY['PERFORMER']), + ('DRESS_TOPLESS', 'Topless', 'Chest uncovered, genitals covered', 'DRESS', 2, ARRAY['PERFORMER']), + ('DRESS_NUDE', 'Nude', 'Unclothed', 'DRESS', 3, ARRAY['PERFORMER']), + ('DRESS_EXPLICIT', 'Explicit', 'Unclothed, with the genital area the direct focus of the image', 'DRESS', 4, ARRAY['PERFORMER']); + +-- Two anatomical lines generate all of these. Topless needs the chest in +-- frame, so it needs Bust or looser. Explicit needs the genital area, and +-- every crop down to and including Torso stops at the hips, so it needs +-- Three-quarter or looser. Face is the exception in kind rather than degree: +-- a face crop is defined by having a face in it, which a shot from behind +-- does not. +INSERT INTO image_type_conflicts ("type_key", "conflicts_with_key") VALUES + ('CROP_FACE', 'DRESS_TOPLESS'), + ('CROP_FACE', 'DRESS_NUDE'), + ('CROP_FACE', 'DRESS_EXPLICIT'), + ('CROP_BUST', 'DRESS_EXPLICIT'), + ('CROP_TORSO', 'DRESS_EXPLICIT'), + ('CROP_FACE', 'VIEW_BACK'); diff --git a/internal/database/migrations/postgres/78_performer_image_types.up.sql b/internal/database/migrations/postgres/78_performer_image_types.up.sql new file mode 100644 index 000000000..6a81f37a1 --- /dev/null +++ b/internal/database/migrations/postgres/78_performer_image_types.up.sql @@ -0,0 +1,17 @@ +-- Type assignments belong to the entity-image relationship rather than to the +-- image: an images row is deduplicated by checksum and shared across performers, +-- scenes and studios, and can legitimately mean different things in each. +-- +-- The composite foreign key is what makes removal correct. Dropping an image +-- from a performer must drop that performer's assignments for it while leaving +-- another performer's -- or a scene's -- assignments for the same image alone. +-- A foreign key to images(id) could not express that. Postgres requires a +-- unique constraint on the referenced columns, which migration 76 added. +CREATE TABLE performer_image_types ( + "performer_id" uuid NOT NULL, + "image_id" uuid NOT NULL, + "type_key" text NOT NULL REFERENCES "image_types"("key"), + PRIMARY KEY ("performer_id", "image_id", "type_key"), + FOREIGN KEY ("performer_id", "image_id") + REFERENCES "performer_images"("performer_id", "image_id") ON DELETE CASCADE +); diff --git a/internal/database/migrations/postgres/79_performer_image_dates.up.sql b/internal/database/migrations/postgres/79_performer_image_dates.up.sql new file mode 100644 index 000000000..ae6374147 --- /dev/null +++ b/internal/database/migrations/postgres/79_performer_image_dates.up.sql @@ -0,0 +1,9 @@ +-- When an image is from, as a partial ISO 8601 string: 2019, +-- 2019-06 or 2019-06-15. This is the idiom the schema already uses for +-- uncertain dates (scenes.production_date, performers.birth_date), rather than +-- the deprecated FuzzyDate pairing of a date with an accuracy column. +-- +-- It sits on the join row rather than on performer_image_types because it is a +-- property of the image's presence on the performer, not of any one label: an +-- image with three labels has one date. +ALTER TABLE performer_images ADD COLUMN "date" text; diff --git a/internal/database/migrations/postgres/80_user_image_type_preferences.up.sql b/internal/database/migrations/postgres/80_user_image_type_preferences.up.sql new file mode 100644 index 000000000..23cb38e01 --- /dev/null +++ b/internal/database/migrations/postgres/80_user_image_type_preferences.up.sql @@ -0,0 +1,32 @@ +-- A per-user reordering of the image types, applied server-side so existing +-- scraping clients benefit without a release. Paired with +-- user_image_type_group_preferences below, which orders the dimensions +-- themselves; this table only orders values inside one. No rows for a user +-- means no preference, which is the default for every existing user; unlike +-- user_notifications this table is deliberately not seeded. +-- +-- No separate user_id index: the primary key's btree already leads with +-- user_id, so it serves every lookup the resolver makes. user_notifications +-- needs one only because it has no primary key at all. +CREATE TABLE user_image_type_preferences ( + "user_id" uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, + "type_key" text NOT NULL REFERENCES image_types("key"), + "sort_order" integer NOT NULL, + PRIMARY KEY ("user_id", "type_key") +); + +-- Which dimension a user compares first, the same choice the admin makes +-- instance-wide. Separate from the type table rather than sharing one with a +-- discriminator: the keys come from different vocabularies and reference +-- different tables, so a single column could not carry both foreign keys. +-- +-- This is the stronger of the two preferences -- group order decides which +-- dimension wins, type order only breaks ties inside one -- so withholding it +-- left preferences unable to express themselves at all whenever the dimension +-- someone cared about was compared last. +CREATE TABLE user_image_type_group_preferences ( + "user_id" uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, + "group_key" text NOT NULL REFERENCES image_type_groups("key"), + "sort_order" integer NOT NULL, + PRIMARY KEY ("user_id", "group_key") +); diff --git a/internal/database/migrations/postgres/81_edit_final_images.up.sql b/internal/database/migrations/postgres/81_edit_final_images.up.sql new file mode 100644 index 000000000..6a0071b5e --- /dev/null +++ b/internal/database/migrations/postgres/81_edit_final_images.up.sql @@ -0,0 +1,54 @@ +-- The images an edit results in, stated once. +-- +-- Three queries need this set -- GetImagesForEdit, GetImageTypesForEdit and +-- GetImageDatesForEdit -- and each carried its own copy of the same +-- twenty-four lines, because sqlc compiles every "-- name:" block into an +-- independent statement and a CTE cannot be shared between them. The copies +-- were byte-identical and commented "keep the copies in step", which they were +-- not: 3.3.7 found one of the three had lost the deduplication the other two +-- had. +-- +-- A view rather than a function taking the edit id, which is what this wants to +-- be. sqlc will not type a table-returning function's columns, so a query +-- selecting from one does not compile -- tried, and it fails on exactly the two +-- queries that select image_id rather than only joining on it. +-- +-- The cost of a view is that the filter arrives from outside, and the planner +-- has to push edit_id down through the UNION and into each branch. It does. On +-- 50k edits with 50k join rows the plan is an Index Scan on edits_pkey with the +-- id as its Index Cond, so one edit's jsonb is expanded rather than fifty +-- thousand +CREATE VIEW edit_final_images AS +WITH current_images AS ( + SELECT se.edit_id, si.image_id + FROM scene_edits se + JOIN scene_images si ON se.scene_id = si.scene_id + UNION ALL + SELECT pe.edit_id, pi.image_id + FROM performer_edits pe + JOIN performer_images pi ON pe.performer_id = pi.performer_id + UNION ALL + SELECT ste.edit_id, sti.image_id + FROM studio_edits ste + JOIN studio_images sti ON ste.studio_id = sti.studio_id +), +removed_images AS ( + SELECT e.id AS edit_id, + jsonb_array_elements_text( + COALESCE(e.data->'new_data'->'removed_images', '[]'::jsonb))::uuid AS image_id + FROM edits e +), +added_images AS ( + SELECT e.id AS edit_id, + jsonb_array_elements_text( + COALESCE(e.data->'new_data'->'added_images', '[]'::jsonb))::uuid AS image_id + FROM edits e +) +SELECT c.edit_id, c.image_id +FROM current_images c +WHERE NOT EXISTS ( + SELECT 1 FROM removed_images r + WHERE r.edit_id = c.edit_id AND r.image_id = c.image_id +) +UNION +SELECT a.edit_id, a.image_id FROM added_images a; diff --git a/internal/dataloader/imagedatesloader_gen.go b/internal/dataloader/imagedatesloader_gen.go new file mode 100644 index 000000000..d10e4d8fe --- /dev/null +++ b/internal/dataloader/imagedatesloader_gen.go @@ -0,0 +1,226 @@ +// Code generated by github.com/vektah/dataloaden, DO NOT EDIT. + +package dataloader + +import ( + "sync" + "time" + + "github.com/gofrs/uuid" + "github.com/stashapp/stash-box/internal/models" +) + +// ImageDatesLoaderConfig captures the config to create a new ImageDatesLoader +type ImageDatesLoaderConfig struct { + // Fetch is a method that provides the data for the loader + Fetch func(keys []uuid.UUID) ([][]models.ImageDate, []error) + + // Wait is how long wait before sending a batch + Wait time.Duration + + // MaxBatch will limit the maximum number of keys to send in one batch, 0 = not limit + MaxBatch int +} + +// NewImageDatesLoader creates a new ImageDatesLoader given a fetch, wait, and maxBatch +func NewImageDatesLoader(config ImageDatesLoaderConfig) *ImageDatesLoader { + return &ImageDatesLoader{ + fetch: config.Fetch, + wait: config.Wait, + maxBatch: config.MaxBatch, + } +} + +// ImageDatesLoader batches and caches requests +type ImageDatesLoader struct { + // this method provides the data for the loader + fetch func(keys []uuid.UUID) ([][]models.ImageDate, []error) + + // how long to done before sending a batch + wait time.Duration + + // this will limit the maximum number of keys to send in one batch, 0 = no limit + maxBatch int + + // INTERNAL + + // lazily created cache + cache map[uuid.UUID][]models.ImageDate + + // the current batch. keys will continue to be collected until timeout is hit, + // then everything will be sent to the fetch method and out to the listeners + batch *imageDatesLoaderBatch + + // mutex to prevent races + mu sync.Mutex +} + +type imageDatesLoaderBatch struct { + keys []uuid.UUID + data [][]models.ImageDate + error []error + closing bool + done chan struct{} +} + +// Load a ImageDate by key, batching and caching will be applied automatically +func (l *ImageDatesLoader) Load(key uuid.UUID) ([]models.ImageDate, error) { + return l.LoadThunk(key)() +} + +// LoadThunk returns a function that when called will block waiting for a ImageDate. +// This method should be used if you want one goroutine to make requests to many +// different data loaders without blocking until the thunk is called. +func (l *ImageDatesLoader) LoadThunk(key uuid.UUID) func() ([]models.ImageDate, error) { + l.mu.Lock() + if it, ok := l.cache[key]; ok { + l.mu.Unlock() + return func() ([]models.ImageDate, error) { + return it, nil + } + } + if l.batch == nil { + l.batch = &imageDatesLoaderBatch{done: make(chan struct{})} + } + batch := l.batch + pos := batch.keyIndex(l, key) + l.mu.Unlock() + + return func() ([]models.ImageDate, error) { + <-batch.done + + var data []models.ImageDate + if pos < len(batch.data) { + data = batch.data[pos] + } + + var err error + // its convenient to be able to return a single error for everything + if len(batch.error) == 1 { + err = batch.error[0] + } else if batch.error != nil { + err = batch.error[pos] + } + + if err == nil { + l.mu.Lock() + l.unsafeSet(key, data) + l.mu.Unlock() + } + + return data, err + } +} + +// LoadAll fetches many keys at once. It will be broken into appropriate sized +// sub batches depending on how the loader is configured +func (l *ImageDatesLoader) LoadAll(keys []uuid.UUID) ([][]models.ImageDate, []error) { + results := make([]func() ([]models.ImageDate, error), len(keys)) + + for i, key := range keys { + results[i] = l.LoadThunk(key) + } + + imageDates := make([][]models.ImageDate, len(keys)) + errors := make([]error, len(keys)) + for i, thunk := range results { + imageDates[i], errors[i] = thunk() + } + return imageDates, errors +} + +// LoadAllThunk returns a function that when called will block waiting for a ImageDates. +// This method should be used if you want one goroutine to make requests to many +// different data loaders without blocking until the thunk is called. +func (l *ImageDatesLoader) LoadAllThunk(keys []uuid.UUID) func() ([][]models.ImageDate, []error) { + results := make([]func() ([]models.ImageDate, error), len(keys)) + for i, key := range keys { + results[i] = l.LoadThunk(key) + } + return func() ([][]models.ImageDate, []error) { + imageDates := make([][]models.ImageDate, len(keys)) + errors := make([]error, len(keys)) + for i, thunk := range results { + imageDates[i], errors[i] = thunk() + } + return imageDates, errors + } +} + +// Prime the cache with the provided key and value. If the key already exists, no change is made +// and false is returned. +// (To forcefully prime the cache, clear the key first with loader.clear(key).prime(key, value).) +func (l *ImageDatesLoader) Prime(key uuid.UUID, value []models.ImageDate) bool { + l.mu.Lock() + var found bool + if _, found = l.cache[key]; !found { + // make a copy when writing to the cache, its easy to pass a pointer in from a loop var + // and end up with the whole cache pointing to the same value. + cpy := make([]models.ImageDate, len(value)) + copy(cpy, value) + l.unsafeSet(key, cpy) + } + l.mu.Unlock() + return !found +} + +// Clear the value at key from the cache, if it exists +func (l *ImageDatesLoader) Clear(key uuid.UUID) { + l.mu.Lock() + delete(l.cache, key) + l.mu.Unlock() +} + +func (l *ImageDatesLoader) unsafeSet(key uuid.UUID, value []models.ImageDate) { + if l.cache == nil { + l.cache = map[uuid.UUID][]models.ImageDate{} + } + l.cache[key] = value +} + +// keyIndex will return the location of the key in the batch, if its not found +// it will add the key to the batch +func (b *imageDatesLoaderBatch) keyIndex(l *ImageDatesLoader, key uuid.UUID) int { + for i, existingKey := range b.keys { + if key == existingKey { + return i + } + } + + pos := len(b.keys) + b.keys = append(b.keys, key) + if pos == 0 { + go b.startTimer(l) + } + + if l.maxBatch != 0 && pos >= l.maxBatch-1 { + if !b.closing { + b.closing = true + l.batch = nil + go b.end(l) + } + } + + return pos +} + +func (b *imageDatesLoaderBatch) startTimer(l *ImageDatesLoader) { + time.Sleep(l.wait) + l.mu.Lock() + + // we must have hit a batch limit and are already finalizing this batch + if b.closing { + l.mu.Unlock() + return + } + + l.batch = nil + l.mu.Unlock() + + b.end(l) +} + +func (b *imageDatesLoaderBatch) end(l *ImageDatesLoader) { + b.data, b.error = l.fetch(b.keys) + close(b.done) +} diff --git a/internal/dataloader/imagetypeassignmentsloader_gen.go b/internal/dataloader/imagetypeassignmentsloader_gen.go new file mode 100644 index 000000000..9a1c730eb --- /dev/null +++ b/internal/dataloader/imagetypeassignmentsloader_gen.go @@ -0,0 +1,226 @@ +// Code generated by github.com/vektah/dataloaden, DO NOT EDIT. + +package dataloader + +import ( + "sync" + "time" + + "github.com/gofrs/uuid" + "github.com/stashapp/stash-box/internal/models" +) + +// ImageTypeAssignmentsLoaderConfig captures the config to create a new ImageTypeAssignmentsLoader +type ImageTypeAssignmentsLoaderConfig struct { + // Fetch is a method that provides the data for the loader + Fetch func(keys []uuid.UUID) ([][]models.ImageTypeAssignment, []error) + + // Wait is how long wait before sending a batch + Wait time.Duration + + // MaxBatch will limit the maximum number of keys to send in one batch, 0 = not limit + MaxBatch int +} + +// NewImageTypeAssignmentsLoader creates a new ImageTypeAssignmentsLoader given a fetch, wait, and maxBatch +func NewImageTypeAssignmentsLoader(config ImageTypeAssignmentsLoaderConfig) *ImageTypeAssignmentsLoader { + return &ImageTypeAssignmentsLoader{ + fetch: config.Fetch, + wait: config.Wait, + maxBatch: config.MaxBatch, + } +} + +// ImageTypeAssignmentsLoader batches and caches requests +type ImageTypeAssignmentsLoader struct { + // this method provides the data for the loader + fetch func(keys []uuid.UUID) ([][]models.ImageTypeAssignment, []error) + + // how long to done before sending a batch + wait time.Duration + + // this will limit the maximum number of keys to send in one batch, 0 = no limit + maxBatch int + + // INTERNAL + + // lazily created cache + cache map[uuid.UUID][]models.ImageTypeAssignment + + // the current batch. keys will continue to be collected until timeout is hit, + // then everything will be sent to the fetch method and out to the listeners + batch *imageTypeAssignmentsLoaderBatch + + // mutex to prevent races + mu sync.Mutex +} + +type imageTypeAssignmentsLoaderBatch struct { + keys []uuid.UUID + data [][]models.ImageTypeAssignment + error []error + closing bool + done chan struct{} +} + +// Load a ImageTypeAssignment by key, batching and caching will be applied automatically +func (l *ImageTypeAssignmentsLoader) Load(key uuid.UUID) ([]models.ImageTypeAssignment, error) { + return l.LoadThunk(key)() +} + +// LoadThunk returns a function that when called will block waiting for a ImageTypeAssignment. +// This method should be used if you want one goroutine to make requests to many +// different data loaders without blocking until the thunk is called. +func (l *ImageTypeAssignmentsLoader) LoadThunk(key uuid.UUID) func() ([]models.ImageTypeAssignment, error) { + l.mu.Lock() + if it, ok := l.cache[key]; ok { + l.mu.Unlock() + return func() ([]models.ImageTypeAssignment, error) { + return it, nil + } + } + if l.batch == nil { + l.batch = &imageTypeAssignmentsLoaderBatch{done: make(chan struct{})} + } + batch := l.batch + pos := batch.keyIndex(l, key) + l.mu.Unlock() + + return func() ([]models.ImageTypeAssignment, error) { + <-batch.done + + var data []models.ImageTypeAssignment + if pos < len(batch.data) { + data = batch.data[pos] + } + + var err error + // its convenient to be able to return a single error for everything + if len(batch.error) == 1 { + err = batch.error[0] + } else if batch.error != nil { + err = batch.error[pos] + } + + if err == nil { + l.mu.Lock() + l.unsafeSet(key, data) + l.mu.Unlock() + } + + return data, err + } +} + +// LoadAll fetches many keys at once. It will be broken into appropriate sized +// sub batches depending on how the loader is configured +func (l *ImageTypeAssignmentsLoader) LoadAll(keys []uuid.UUID) ([][]models.ImageTypeAssignment, []error) { + results := make([]func() ([]models.ImageTypeAssignment, error), len(keys)) + + for i, key := range keys { + results[i] = l.LoadThunk(key) + } + + imageTypeAssignments := make([][]models.ImageTypeAssignment, len(keys)) + errors := make([]error, len(keys)) + for i, thunk := range results { + imageTypeAssignments[i], errors[i] = thunk() + } + return imageTypeAssignments, errors +} + +// LoadAllThunk returns a function that when called will block waiting for a ImageTypeAssignments. +// This method should be used if you want one goroutine to make requests to many +// different data loaders without blocking until the thunk is called. +func (l *ImageTypeAssignmentsLoader) LoadAllThunk(keys []uuid.UUID) func() ([][]models.ImageTypeAssignment, []error) { + results := make([]func() ([]models.ImageTypeAssignment, error), len(keys)) + for i, key := range keys { + results[i] = l.LoadThunk(key) + } + return func() ([][]models.ImageTypeAssignment, []error) { + imageTypeAssignments := make([][]models.ImageTypeAssignment, len(keys)) + errors := make([]error, len(keys)) + for i, thunk := range results { + imageTypeAssignments[i], errors[i] = thunk() + } + return imageTypeAssignments, errors + } +} + +// Prime the cache with the provided key and value. If the key already exists, no change is made +// and false is returned. +// (To forcefully prime the cache, clear the key first with loader.clear(key).prime(key, value).) +func (l *ImageTypeAssignmentsLoader) Prime(key uuid.UUID, value []models.ImageTypeAssignment) bool { + l.mu.Lock() + var found bool + if _, found = l.cache[key]; !found { + // make a copy when writing to the cache, its easy to pass a pointer in from a loop var + // and end up with the whole cache pointing to the same value. + cpy := make([]models.ImageTypeAssignment, len(value)) + copy(cpy, value) + l.unsafeSet(key, cpy) + } + l.mu.Unlock() + return !found +} + +// Clear the value at key from the cache, if it exists +func (l *ImageTypeAssignmentsLoader) Clear(key uuid.UUID) { + l.mu.Lock() + delete(l.cache, key) + l.mu.Unlock() +} + +func (l *ImageTypeAssignmentsLoader) unsafeSet(key uuid.UUID, value []models.ImageTypeAssignment) { + if l.cache == nil { + l.cache = map[uuid.UUID][]models.ImageTypeAssignment{} + } + l.cache[key] = value +} + +// keyIndex will return the location of the key in the batch, if its not found +// it will add the key to the batch +func (b *imageTypeAssignmentsLoaderBatch) keyIndex(l *ImageTypeAssignmentsLoader, key uuid.UUID) int { + for i, existingKey := range b.keys { + if key == existingKey { + return i + } + } + + pos := len(b.keys) + b.keys = append(b.keys, key) + if pos == 0 { + go b.startTimer(l) + } + + if l.maxBatch != 0 && pos >= l.maxBatch-1 { + if !b.closing { + b.closing = true + l.batch = nil + go b.end(l) + } + } + + return pos +} + +func (b *imageTypeAssignmentsLoaderBatch) startTimer(l *ImageTypeAssignmentsLoader) { + time.Sleep(l.wait) + l.mu.Lock() + + // we must have hit a batch limit and are already finalizing this batch + if b.closing { + l.mu.Unlock() + return + } + + l.batch = nil + l.mu.Unlock() + + b.end(l) +} + +func (b *imageTypeAssignmentsLoaderBatch) end(l *ImageTypeAssignmentsLoader) { + b.data, b.error = l.fetch(b.keys) + close(b.done) +} diff --git a/internal/dataloader/loaders.go b/internal/dataloader/loaders.go index d3d9b0743..6a4e75b92 100644 --- a/internal/dataloader/loaders.go +++ b/internal/dataloader/loaders.go @@ -3,12 +3,14 @@ package dataloader import ( "context" "net/http" + "sync" "time" "github.com/gofrs/uuid" "github.com/stashapp/stash-box/internal/auth" "github.com/stashapp/stash-box/internal/models" "github.com/stashapp/stash-box/internal/service" + "github.com/stashapp/stash-box/internal/service/imagetype" ) type contextKey int @@ -24,6 +26,8 @@ type Loaders struct { PerformerByID PerformerLoader PerformerAliasesByID StringsLoader PerformerImageIDsByID UUIDsLoader + PerformerImageTypesByID ImageTypeAssignmentsLoader + PerformerImageDatesByID ImageDatesLoader PerformerMergeIDsByID UUIDsLoader PerformerMergeIDsBySourceID UUIDsLoader PerformerPiercingsByID BodyModificationsLoader @@ -51,6 +55,26 @@ type Loaders struct { SceneEditsByID EditsLoader EditCommentByID EditCommentLoader UserByID UserLoader + + // Not keyed by anything: every entity in a result ranks against the same + // vocabulary, so it is read at most once per request rather than once per + // entity. Loaded lazily, since most requests never rank an image. + ImageTypeVocabulary *VocabularyMemo +} + +// VocabularyMemo loads the image type ordering at most once, on first use. +type VocabularyMemo struct { + once sync.Once + load func() (*imagetype.Vocabulary, error) + value *imagetype.Vocabulary + err error +} + +func (m *VocabularyMemo) Get() (*imagetype.Vocabulary, error) { + m.once.Do(func() { + m.value, m.err = m.load() + }) + return m.value, m.err } func Middleware(fac service.Factory) func(next http.Handler) http.Handler { @@ -115,6 +139,27 @@ func GetLoaders(ctx context.Context, fac service.Factory) *Loaders { return s.LoadByPerformerIds(ctx, ids) }, }, + PerformerImageTypesByID: ImageTypeAssignmentsLoader{ + maxBatch: 100, + wait: 1 * time.Millisecond, + fetch: func(ids []uuid.UUID) ([][]models.ImageTypeAssignment, []error) { + s := fac.ImageType() + return s.LoadAssignmentsByPerformerIds(ctx, ids) + }, + }, + ImageTypeVocabulary: &VocabularyMemo{ + load: func() (*imagetype.Vocabulary, error) { + return fac.ImageType().VocabularyFor(ctx, currentUser.ID) + }, + }, + PerformerImageDatesByID: ImageDatesLoader{ + maxBatch: 100, + wait: 1 * time.Millisecond, + fetch: func(ids []uuid.UUID) ([][]models.ImageDate, []error) { + s := fac.ImageType() + return s.LoadDatesByPerformerIds(ctx, ids) + }, + }, PerformerMergeIDsByID: UUIDsLoader{ maxBatch: 100, wait: 1 * time.Millisecond, diff --git a/internal/image/crop.go b/internal/image/crop.go new file mode 100644 index 000000000..6e126f21c --- /dev/null +++ b/internal/image/crop.go @@ -0,0 +1,91 @@ +package image + +import ( + "errors" + "fmt" + "math" +) + +// CropRect is a frame to cut an image down to, as fractions of the image the +// client is looking at +// +// Fractions rather than pixels because the client is dragging a frame over a +// scaled preview and does not know, or need to know, the source resolution +type CropRect struct { + X float64 + Y float64 + Width float64 + Height float64 + + // Angle is degrees clockwise, applied before the frame is cut. X, Y, Width + // and Height are measured against the *rotated* image, which is larger + // than the original - it is what the client is dragging over + Angle float64 +} + +// ErrCropUnsupportedFormat reports an upload that cannot be cropped. SVG is the +// case that matters: it has no pixels to cut and no dimensions worth speaking +// of, which is why it is stored with -1 for both +var ErrCropUnsupportedFormat = errors.New("this image format cannot be cropped") + +const maxCropAngle = 90 + +// Validate rejects a frame that cannot describe part of an image +func (c CropRect) Validate() error { + for name, v := range map[string]float64{ + "x": c.X, "y": c.Y, "width": c.Width, "height": c.Height, "angle": c.Angle, + } { + if math.IsNaN(v) || math.IsInf(v, 0) { + return fmt.Errorf("crop %s is not a number", name) + } + } + + if c.Width <= 0 || c.Height <= 0 { + return fmt.Errorf("crop has no area: %gx%g", c.Width, c.Height) + } + if c.X < 0 || c.Y < 0 || c.X+c.Width > 1 || c.Y+c.Height > 1 { + return fmt.Errorf("crop (%g, %g) %gx%g falls outside the image", c.X, c.Y, c.Width, c.Height) + } + if math.Abs(c.Angle) > maxCropAngle { + return fmt.Errorf("crop angle %g is beyond %d degrees", c.Angle, maxCropAngle) + } + + return nil +} + +// IsIdentity reports a frame that would keep the whole image unchanged. +// +// Worth spotting: cropping means re-encoding, and re-encoding an image nobody +// actually cropped costs a generation of quality for nothing +func (c CropRect) IsIdentity() bool { + return c.Angle == 0 && c.X == 0 && c.Y == 0 && c.Width == 1 && c.Height == 1 +} + +// pixels turns the frame into whole pixels within a width x height image +// +// Rounding outward would let a frame at the very edge ask for a pixel that is +// not there, so the result is clamped to the image and to at least one pixel in +// each direction. A crop of nothing is caught by Validate; this is the +// arithmetic being careful, not policy +func (c CropRect) pixels(width, height int) (left, top, w, h int) { + left = clamp(int(math.Round(c.X*float64(width))), 0, width-1) + top = clamp(int(math.Round(c.Y*float64(height))), 0, height-1) + w = clamp(int(math.Round(c.Width*float64(width))), 1, width-left) + h = clamp(int(math.Round(c.Height*float64(height))), 1, height-top) + return left, top, w, h +} + +func clamp(v, lo, hi int) int { + if hi < lo { + hi = lo + } + return min(max(v, lo), hi) +} + +// croppedQuality is what a cropped upload is re-encoded at, for every lossy +// format. Higher than the thumbnail quality in config, since this is the copy +// everything else is derived from +// +// Lossless is not the cautious choice it looks like: a photograph stored that +// way runs several times the size it arrived at +const croppedQuality = 95 diff --git a/internal/image/crop_novips.go b/internal/image/crop_novips.go new file mode 100644 index 000000000..04d7c6907 --- /dev/null +++ b/internal/image/crop_novips.go @@ -0,0 +1,100 @@ +//go:build windows + +package image + +import ( + "bytes" + "image" + "image/color" + "image/draw" + "image/jpeg" + "image/png" + "math" +) + +// white fills the corners a rotation exposes +var white = color.NRGBA{R: 255, G: 255, B: 255, A: 255} + +// Crop cuts an upload down to a frame, rotating it first if asked +// +// The pure-Go counterpart to the libvips path, for builds without it. Same +// order of operations, with one difference that cannot be helped: Go's image +// decoders do not apply EXIF orientation, so an upload whose orientation lives +// only in metadata crops from the unrotated pixels. The libvips build is what +// production uses; this keeps a developer on macOS or Windows able to run the +// thing +func Crop(data []byte, rect CropRect) ([]byte, error) { + if err := rect.Validate(); err != nil { + return nil, err + } + + src, format, err := image.Decode(bytes.NewReader(data)) + if err != nil { + return nil, ErrCropUnsupportedFormat + } + if format != "jpeg" && format != "png" { + return nil, ErrCropUnsupportedFormat + } + + if rect.Angle != 0 { + src = rotate(src, rect.Angle) + } + + bounds := src.Bounds() + left, top, width, height := rect.pixels(bounds.Dx(), bounds.Dy()) + + frame := image.Rect(0, 0, width, height) + out := image.NewNRGBA(frame) + draw.Draw(out, frame, src, bounds.Min.Add(image.Pt(left, top)), draw.Src) + + buf := new(bytes.Buffer) + if format == "png" { + if err := png.Encode(buf, out); err != nil { + return nil, err + } + return buf.Bytes(), nil + } + if err := jpeg.Encode(buf, out, &jpeg.Options{Quality: croppedQuality}); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// rotate turns an image clockwise about its centre, growing the canvas to fit, +// so the result matches what libvips produces for the same angle +// +// Nearest-neighbour: this path exists so the application runs, not so it +// produces the best possible pixels, and the alternative is hand-rolling a +// resampling filter that libvips already has +func rotate(src image.Image, degrees float64) image.Image { + bounds := src.Bounds() + w, h := float64(bounds.Dx()), float64(bounds.Dy()) + + radians := degrees * math.Pi / 180 + sin, cos := math.Abs(math.Sin(radians)), math.Abs(math.Cos(radians)) + outW := int(math.Round(w*cos + h*sin)) + outH := int(math.Round(w*sin + h*cos)) + + out := image.NewNRGBA(image.Rect(0, 0, outW, outH)) + draw.Draw(out, out.Bounds(), image.NewUniform(white), image.Point{}, draw.Src) + + // Walk the destination and pull from the source, which leaves no gaps the + // way walking the source and pushing would + srcCx, srcCy := w/2, h/2 + dstCx, dstCy := float64(outW)/2, float64(outH)/2 + rotSin, rotCos := math.Sin(radians), math.Cos(radians) + + for y := range outH { + for x := range outW { + dx, dy := float64(x)-dstCx, float64(y)-dstCy + sx := int(math.Round(dx*rotCos + dy*rotSin + srcCx)) + sy := int(math.Round(-dx*rotSin + dy*rotCos + srcCy)) + if sx < 0 || sy < 0 || sx >= bounds.Dx() || sy >= bounds.Dy() { + continue + } + out.Set(x, y, src.At(bounds.Min.X+sx, bounds.Min.Y+sy)) + } + } + + return out +} diff --git a/internal/image/crop_test.go b/internal/image/crop_test.go new file mode 100644 index 000000000..f4b7cb75b --- /dev/null +++ b/internal/image/crop_test.go @@ -0,0 +1,128 @@ +package image + +import ( + "math" + "testing" +) + +func full() CropRect { return CropRect{X: 0, Y: 0, Width: 1, Height: 1} } + +func TestCropRectValidateAcceptsUsableFrames(t *testing.T) { + for _, tc := range []struct { + name string + rect CropRect + }{ + {"the whole image", full()}, + {"a corner", CropRect{X: 0, Y: 0, Width: 0.5, Height: 0.5}}, + {"flush against the far edge", CropRect{X: 0.5, Y: 0.5, Width: 0.5, Height: 0.5}}, + {"a sliver", CropRect{X: 0.4, Y: 0.4, Width: 0.001, Height: 0.001}}, + {"straightening a horizon", CropRect{X: 0.1, Y: 0.1, Width: 0.8, Height: 0.8, Angle: 2.5}}, + {"turned the other way", CropRect{X: 0.1, Y: 0.1, Width: 0.8, Height: 0.8, Angle: -2.5}}, + {"a quarter turn, the most allowed", CropRect{X: 0, Y: 0, Width: 1, Height: 1, Angle: 90}}, + } { + t.Run(tc.name, func(t *testing.T) { + if err := tc.rect.Validate(); err != nil { + t.Errorf("rejected a usable frame: %v", err) + } + }) + } +} + +func TestCropRectValidateRejectsUnusableFrames(t *testing.T) { + for _, tc := range []struct { + name string + rect CropRect + }{ + {"no width", CropRect{X: 0, Y: 0, Width: 0, Height: 1}}, + {"no height", CropRect{X: 0, Y: 0, Width: 1, Height: 0}}, + {"negative width", CropRect{X: 0, Y: 0, Width: -0.5, Height: 1}}, + {"starts left of the image", CropRect{X: -0.1, Y: 0, Width: 0.5, Height: 1}}, + {"starts above the image", CropRect{X: 0, Y: -0.1, Width: 1, Height: 0.5}}, + {"runs off the right", CropRect{X: 0.6, Y: 0, Width: 0.5, Height: 1}}, + {"runs off the bottom", CropRect{X: 0, Y: 0.6, Width: 1, Height: 0.5}}, + {"wider than the image", CropRect{X: 0, Y: 0, Width: 1.5, Height: 1}}, + {"turned too far", CropRect{X: 0, Y: 0, Width: 1, Height: 1, Angle: 91}}, + {"turned too far the other way", CropRect{X: 0, Y: 0, Width: 1, Height: 1, Angle: -91}}, + {"a position that is not a number", CropRect{X: math.NaN(), Y: 0, Width: 1, Height: 1}}, + {"a size that is not a number", CropRect{X: 0, Y: 0, Width: math.NaN(), Height: 1}}, + {"an infinite angle", CropRect{X: 0, Y: 0, Width: 1, Height: 1, Angle: math.Inf(1)}}, + } { + t.Run(tc.name, func(t *testing.T) { + if err := tc.rect.Validate(); err == nil { + t.Error("accepted an unusable frame") + } + }) + } +} + +func TestCropRectIsIdentity(t *testing.T) { + if !full().IsIdentity() { + t.Error("the whole image should be the identity") + } + for _, rect := range []CropRect{ + {X: 0, Y: 0, Width: 1, Height: 1, Angle: 1}, + {X: 0.1, Y: 0, Width: 0.9, Height: 1}, + {X: 0, Y: 0, Width: 1, Height: 0.9}, + } { + if rect.IsIdentity() { + t.Errorf("%+v should not be the identity", rect) + } + } +} + +func TestCropRectPixels(t *testing.T) { + for _, tc := range []struct { + name string + rect CropRect + width, height int + left, top, wantW, wantH int + }{ + {"the whole image", full(), 800, 1200, 0, 0, 800, 1200}, + {"the top-left quarter", + CropRect{X: 0, Y: 0, Width: 0.5, Height: 0.5}, 800, 1200, 0, 0, 400, 600}, + {"the bottom-right quarter", + CropRect{X: 0.5, Y: 0.5, Width: 0.5, Height: 0.5}, 800, 1200, 400, 600, 400, 600}, + {"a middle band", + CropRect{X: 0.25, Y: 0.25, Width: 0.5, Height: 0.5}, 800, 1200, 200, 300, 400, 600}, + } { + t.Run(tc.name, func(t *testing.T) { + left, top, w, h := tc.rect.pixels(tc.width, tc.height) + if left != tc.left || top != tc.top || w != tc.wantW || h != tc.wantH { + t.Errorf("got (%d, %d) %dx%d, want (%d, %d) %dx%d", + left, top, w, h, tc.left, tc.top, tc.wantW, tc.wantH) + } + }) + } +} + +// Rounding a fraction onto whole pixels must never ask for a pixel past the +// edge which is what an extractor would refuse +func TestCropRectPixelsStayInsideTheImage(t *testing.T) { + sizes := []int{1, 2, 3, 7, 33, 100, 799, 800, 1201} + fractions := []float64{0, 0.001, 0.1, 1.0 / 3.0, 0.5, 0.667, 0.9, 0.999} + + for _, width := range sizes { + for _, height := range sizes { + for _, x := range fractions { + for _, w := range fractions { + if w <= 0 || x+w > 1 { + continue + } + rect := CropRect{X: x, Y: x, Width: w, Height: w} + if err := rect.Validate(); err != nil { + continue + } + + left, top, gotW, gotH := rect.pixels(width, height) + if left < 0 || top < 0 || gotW < 1 || gotH < 1 { + t.Fatalf("%dx%d %+v gave (%d, %d) %dx%d", width, height, rect, left, top, gotW, gotH) + } + if left+gotW > width || top+gotH > height { + t.Fatalf("%dx%d %+v reaches past the edge: (%d, %d) %dx%d", + width, height, rect, left, top, gotW, gotH) + } + } + } + } + } +} diff --git a/internal/image/crop_unix.go b/internal/image/crop_unix.go new file mode 100644 index 000000000..2c0f633ef --- /dev/null +++ b/internal/image/crop_unix.go @@ -0,0 +1,92 @@ +//go:build unix + +package image + +import ( + "github.com/davidbyttow/govips/v2/vips" +) + +// Crop cuts an upload down to a frame, rotating it first if asked +// +// One decode and one encode, which is the whole reason this is not done in the +// browser: a canvas crop would be a second lossy generation on top of whatever +// the contributor started with +// +// Order matters and is not interchangeable: +// +// 1. EXIF orientation, so the coordinates the client sent (which are the +// ones shown in the browser) mean the same thing here. Without this every +// photograph taken on a phone crops sideways +// 2. The rotation, which grows the canvas to fit the turned image +// 3. The frame, measured against that grown canvas +func Crop(data []byte, rect CropRect) ([]byte, error) { + defer vips.ShutdownThread() + + if err := rect.Validate(); err != nil { + return nil, err + } + + image, err := vips.NewImageFromBuffer(data) + if err != nil { + return nil, ErrCropUnsupportedFormat + } + defer image.Close() + + format := image.Format() + + if err := image.AutoRotate(); err != nil { + return nil, err + } + + if rect.Angle != 0 { + // White rather than transparent: the corners a rotation exposes are + // going to be cropped away in the ordinary case, and a JPEG has no + // alpha to put them in anyway + white := &vips.ColorRGBA{R: 255, G: 255, B: 255, A: 255} + if err := image.Similarity(1, rect.Angle, white, 0, 0, 0, 0); err != nil { + return nil, err + } + } + + left, top, width, height := rect.pixels(image.Width(), image.Height()) + if err := image.ExtractArea(left, top, width, height); err != nil { + return nil, err + } + + return exportCropped(image, format) +} + +// exportCropped re-encodes in the format that came in +// +// Changing format here would be a surprise: this is the image that gets stored +// and served, not a derived thumbnail, and the resizing path is free to make +// its own choices because nothing keeps what it produces +func exportCropped(image *vips.ImageRef, format vips.ImageType) ([]byte, error) { + switch format { + case vips.ImageTypePNG: + params := vips.NewPngExportParams() + params.StripMetadata = true + out, _, err := image.ExportPng(params) + return out, err + + case vips.ImageTypeWEBP: + params := vips.NewWebpExportParams() + params.StripMetadata = true + params.Quality = croppedQuality + out, _, err := image.ExportWebp(params) + return out, err + + case vips.ImageTypeJPEG: + params := vips.NewJpegExportParams() + params.StripMetadata = true + params.Quality = croppedQuality + params.Interlace = true + params.OptimizeCoding = true + params.SubsampleMode = vips.VipsForeignSubsampleAuto + out, _, err := image.ExportJpeg(params) + return out, err + + default: + return nil, ErrCropUnsupportedFormat + } +} diff --git a/internal/image/crop_unix_test.go b/internal/image/crop_unix_test.go new file mode 100644 index 000000000..9d91179ae --- /dev/null +++ b/internal/image/crop_unix_test.go @@ -0,0 +1,90 @@ +//go:build unix + +package image + +import ( + "image" + "math/rand" + "testing" + + "github.com/davidbyttow/govips/v2/vips" +) + +// photoLike builds a source with the properties that separate a photograph +// from a diagram: smooth gradients, and noise on top of them. A flat test +// pattern compresses the same way whatever the encoder is told to do, so it +// would not notice the setting this file exists to check +func photoLike(t *testing.T, width, height int, export func(*vips.ImageRef) ([]byte, error)) []byte { + t.Helper() + if err := vips.Startup(nil); err != nil { + t.Fatal(err) + } + + img := image.NewRGBA(image.Rect(0, 0, width, height)) + rng := rand.New(rand.NewSource(1)) + for y := range height { + for x := range width { + i := (y*width + x) * 4 + img.Pix[i] = byte((x*255/width + rng.Intn(24)) % 256) + img.Pix[i+1] = byte((y*255/height + rng.Intn(24)) % 256) + img.Pix[i+2] = byte(((x+y)*255/(width+height) + rng.Intn(24)) % 256) + img.Pix[i+3] = 255 + } + } + + ref, err := vips.NewImageFromGoImage(img) + if err != nil { + t.Fatal(err) + } + + out, err := export(ref) + if err != nil { + t.Fatal(err) + } + return out +} + +// The size limit is checked on the way in, on the bytes the client sent, so an +// encoding that multiplies them lands an in-policy upload well over the cap. +// Storing a photograph as lossless WebP does exactly that +func TestCropDoesNotInflateWebp(t *testing.T) { + source := photoLike(t, 2000, 3000, func(ref *vips.ImageRef) ([]byte, error) { + out, _, err := ref.ExportWebp(vips.NewWebpExportParams()) + return out, err + }) + + cropped, err := Crop(source, CropRect{X: 0, Y: 0, Width: 1, Height: 1, Angle: 1}) + if err != nil { + t.Fatal(err) + } + + ratio := float64(len(cropped)) / float64(len(source)) + if ratio > 3 { + t.Errorf("cropping grew a %d byte webp to %d (%.2fx); is it being re-encoded losslessly?", + len(source), len(cropped), ratio) + } +} + +// The stored master keeps the format it arrived in, so nothing downstream has +// to guess: a webp upload stays a webp +func TestCropKeepsTheFormatItWasGiven(t *testing.T) { + source := photoLike(t, 400, 600, func(ref *vips.ImageRef) ([]byte, error) { + out, _, err := ref.ExportWebp(vips.NewWebpExportParams()) + return out, err + }) + + cropped, err := Crop(source, CropRect{X: 0, Y: 0, Width: 0.5, Height: 0.5}) + if err != nil { + t.Fatal(err) + } + + ref, err := vips.NewImageFromBuffer(cropped) + if err != nil { + t.Fatal(err) + } + defer ref.Close() + + if got := ref.Format(); got != vips.ImageTypeWEBP { + t.Errorf("cropped a webp and got %v back", got) + } +} diff --git a/internal/image/croptemplate/container.go b/internal/image/croptemplate/container.go new file mode 100644 index 000000000..87062c2c5 --- /dev/null +++ b/internal/image/croptemplate/container.go @@ -0,0 +1,319 @@ +package croptemplate + +import ( + "fmt" + "unicode/utf16" +) + +// This file walks the PSD container: the sections, lengths and signatures that +// stand between the start of the file and the three payloads this package +// actually wants - the guides resource, the XMP resource, and each layer's +// vector mask. Section names and offsets are from the Adobe Photoshop File +// Formats Specification; see the reference at the top of psd.go's constants. +// +// Only what a template can contain is read. PSB (version 2, 64-bit section +// lengths) is refused rather than handled: a crop template is a small RGB +// canvas, and accepting the large-document format would double every length +// field below for files nobody can ship + +// psdFile is the part of a parsed container the rest of the package reads. +type psdFile struct { + width int + height int + // res maps an image resource ID to its raw payload + res map[int][]byte + // layers is the root of the layer tree, in file order - bottom layer + // first, children of a group nested under it in the same order + layers []psdLayer +} + +type psdLayer struct { + name string + flags uint8 + // info maps a four-character additional-info key to its raw payload + info map[string][]byte + children []psdLayer +} + +// visible reads the layer's own flag. Photoshop leaves a child's flag alone +// when its folder is switched off, so a caller walking the tree has to stop at +// a hidden group rather than trust each child +func (l psdLayer) visible() bool { + return l.flags&2 == 0 +} + +// "File header section": signature, then a version that is 1 for PSD and 2 +// for PSB +const ( + psdSignature = "8BPS" + psdVersion = 1 +) + +// "Layer records", flags byte: bit 1 is "visible", set means hidden + +// decode walks a whole container. Nothing pixel-shaped is ever read: the +// section lengths let every raster be stepped over, and parsing stops after +// the layer records, before the channel image data they describe +func decode(data []byte) (psdFile, error) { + r := &reader{buf: data} + + if sig := string(r.bytes(4)); r.err != nil || sig != psdSignature { + return psdFile{}, fmt.Errorf("not a PSD file: signature %q", sig) + } + if version := r.uint16(); r.err != nil || version != psdVersion { + return psdFile{}, fmt.Errorf("unsupported PSD version %d", version) + } + + r.skip(6) // reserved + r.skip(2) // channel count + height := int(r.uint32()) + width := int(r.uint32()) + r.skip(2) // bit depth + r.skip(2) // colour mode + + // "Color Mode Data Section": a length and a payload only indexed-colour + // and duotone files fill in + r.skip(int(r.uint32())) + + resources, err := parseResources(r.bytes(int(r.uint32()))) + if err == nil { + err = r.err + } + if err != nil { + return psdFile{}, err + } + + // "Layer and Mask Information Section". Absent entirely in a flattened + // file, which still makes a usable template if it carries guides + var layers []psdLayer + if r.remaining() >= 4 { + if layers, err = parseLayerSection(r.bytes(int(r.uint32()))); err == nil { + err = r.err + } + if err != nil { + return psdFile{}, err + } + } + + return psdFile{width: width, height: height, res: resources, layers: layers}, nil +} + +// parseResources walks the "Image Resources Section": a run of blocks, each a +// signature, an ID, a PascalCase name and a length-prefixed payload, name and +// payload each padded to an even length +func parseResources(block []byte) (map[int][]byte, error) { + r := &reader{buf: block} + resources := map[int][]byte{} + + for r.remaining() > 0 { + if sig := string(r.bytes(4)); r.err != nil || sig != "8BIM" { + return nil, fmt.Errorf("image resource block has signature %q, want 8BIM", sig) + } + id := int(r.uint16()) + + nameLen := int(r.uint8()) + r.skip(nameLen) + if (1+nameLen)%2 != 0 { + r.skip(1) + } + + size := int(r.uint32()) + data := r.bytes(size) + if size%2 != 0 { + r.skip(1) + } + if r.err != nil { + return nil, r.err + } + + resources[id] = data + } + + return resources, nil +} + +// parseLayerSection reads the "Layer Info" sub-section far enough to have +// every layer record, then assembles the tree. The channel image data that +// follows the records is never touched +func parseLayerSection(section []byte) ([]psdLayer, error) { + r := &reader{buf: section} + if r.remaining() < 4 { + return nil, nil + } + + info := &reader{buf: r.bytes(int(r.uint32()))} + if r.err != nil { + return nil, r.err + } + if info.remaining() < 2 { + return nil, nil + } + + // "Layer count. If it is a negative number, its absolute value is the + // number of layers and the first alpha channel contains the transparency + // data for the merged result." + count := int(int16(info.uint16())) + if count < 0 { + count = -count + } + + flat := make([]psdLayer, 0, count) + for range count { + layer, err := parseLayerRecord(info) + if err != nil { + return nil, err + } + flat = append(flat, layer) + } + + return layerTree(flat) +} + +// parseLayerRecord reads one entry of the layer records array: bounds and +// channels to step over, the flags, and the extra data holding the name and +// the additional-info blocks +func parseLayerRecord(r *reader) (psdLayer, error) { + r.skip(16) // bounds + + // Channel info: 2 bytes of ID and 4 of data length per channel. The + // lengths describe the image data after the records, which is never read + channels := int(r.uint16()) + r.skip(channels * 6) + + if sig := string(r.bytes(4)); r.err == nil && sig != "8BIM" { + return psdLayer{}, fmt.Errorf("layer record has blend signature %q, want 8BIM", sig) + } + r.skip(4) // blend mode key + r.skip(2) // opacity, clipping + flags := r.uint8() + r.skip(1) // filler + + extra := &reader{buf: r.bytes(int(r.uint32()))} + if r.err != nil { + return psdLayer{}, r.err + } + + extra.skip(int(extra.uint32())) // layer mask data + extra.skip(int(extra.uint32())) // blending ranges + + // The legacy name is a PascalCase string padded so it and its length byte + // occupy a multiple of four here, not two as elsewhere + nameLen := int(extra.uint8()) + name := string(extra.bytes(nameLen)) + if pad := (1 + nameLen) % 4; pad != 0 { + extra.skip(4 - pad) + } + + info, err := parseAdditionalInfo(extra) + if err != nil { + return psdLayer{}, err + } + if extra.err != nil { + return psdLayer{}, extra.err + } + + // The Unicode name wins over the legacy one, which Photoshop truncates + // and transliterates + if data, ok := info["luni"]; ok { + if unicode, ok := unicodeString(data); ok { + name = unicode + } + delete(info, "luni") + } + + return psdLayer{name: name, flags: flags, info: info}, nil +} + +// parseAdditionalInfo walks the additional-info blocks at the end of a layer +// record: a signature, a four-character key, and a length-prefixed payload +// +// The spec pads these blocks inconsistently - to two bytes in some producers, +// four in others, not at all in more - so, like every other reader of this +// format, this scans forward to the next signature byte rather than trusting +// any one padding rule +func parseAdditionalInfo(r *reader) (map[string][]byte, error) { + info := map[string][]byte{} + + for r.remaining() > 0 { + for r.remaining() > 0 && r.buf[r.pos] != '8' { + r.skip(1) + } + if r.remaining() < 12 { + break + } + + if sig := string(r.bytes(4)); sig != "8BIM" && sig != "8B64" { + return nil, fmt.Errorf("additional info block has signature %q", sig) + } + key := string(r.bytes(4)) + data := r.bytes(int(r.uint32())) + if r.err != nil { + return nil, r.err + } + + info[key] = data + } + + return info, nil +} + +// layerTree folds the flat record list into a tree using the section divider +// key: a type-3 divider closes off the run of layers that will become a +// group's children, and the group layer itself (type 1 or 2, open or closed +// folder) follows them and claims the run. File order is preserved throughout, +// because shape order is presentation order +func layerTree(flat []psdLayer) ([]psdLayer, error) { + var stack [][]psdLayer + current := []psdLayer{} + + for i, layer := range flat { + switch dividerType(layer) { + case 1, 2: + layer.children = current + if len(stack) == 0 { + return nil, fmt.Errorf("layer %d closes a group nothing opened", i) + } + current = stack[len(stack)-1] + stack = stack[:len(stack)-1] + current = append(current, layer) + case 3: + stack = append(stack, current) + current = []psdLayer{} + default: + current = append(current, layer) + } + } + + if len(stack) != 0 { + return nil, fmt.Errorf("%d group(s) opened and never closed", len(stack)) + } + return current, nil +} + +// dividerType reads the "Section divider setting" of a layer: 0 for an +// ordinary layer, 1 or 2 for a group, 3 for the hidden marker that bounds one +func dividerType(layer psdLayer) uint32 { + for _, key := range []string{"lsct", "lsdk"} { + if data, ok := layer.info[key]; ok && len(data) >= 4 { + return uint32(data[0])<<24 | uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) + } + } + return 0 +} + +// unicodeString reads a luni payload: a character count, then UTF-16 +func unicodeString(data []byte) (string, bool) { + if len(data) < 4 { + return "", false + } + count := int(uint32(data[0])<<24 | uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])) + if count < 0 || len(data)-4 < count*2 { + return "", false + } + + units := make([]uint16, count) + for i := range units { + units[i] = uint16(data[4+i*2])<<8 | uint16(data[5+i*2]) + } + return string(utf16.Decode(units)), true +} diff --git a/internal/image/croptemplate/defaults.go b/internal/image/croptemplate/defaults.go new file mode 100644 index 000000000..4317a53ea --- /dev/null +++ b/internal/image/croptemplate/defaults.go @@ -0,0 +1,49 @@ +package croptemplate + +import ( + "embed" + "fmt" + "path" + "strings" + "sync" +) + +// The built-in templates, one per Crop type, named for the image type key they +// belong to. Embedded so the feature works with no setup at all +// +//go:embed templates/*.psd +var defaultTemplates embed.FS + +const templateDir = "templates" + +// TemplateExt is the extension a template file carries +const TemplateExt = ".psd" + +// Defaults parses the embedded templates, once +// +// An error means a file that shipped in the binary does not parse, which is a +// build fault: the tests parse every one of them and check its guides against +// the documented geometry, so this cannot reach a release +var Defaults = sync.OnceValues(func() (map[string]Template, error) { + entries, err := defaultTemplates.ReadDir(templateDir) + if err != nil { + return nil, err + } + + out := make(map[string]Template, len(entries)) + for _, entry := range entries { + name := entry.Name() + data, err := defaultTemplates.ReadFile(path.Join(templateDir, name)) + if err != nil { + return nil, err + } + + template, err := Parse(data) + if err != nil { + return nil, fmt.Errorf("built-in template %s: %w", name, err) + } + out[strings.TrimSuffix(name, TemplateExt)] = template + } + + return out, nil +}) diff --git a/internal/image/croptemplate/loader.go b/internal/image/croptemplate/loader.go new file mode 100644 index 000000000..d226323ec --- /dev/null +++ b/internal/image/croptemplate/loader.go @@ -0,0 +1,43 @@ +package croptemplate + +import ( + "path" +) + +// Loader resolves a crop type to its template. Templates are embedded in the +// binary, so the feature works with no setup and there is nothing to configure +type Loader struct{} + +// NewLoader returns a loader over the built-in templates +func NewLoader() *Loader { + return &Loader{} +} + +// Template returns the template for a crop type, and whether one exists +func (l *Loader) Template(key string) (Template, bool) { + defaults, err := Defaults() + if err != nil { + return Template{}, false + } + template, ok := defaults[key] + return template, ok +} + +// Bytes returns the template file itself, for downloading: the same bytes the +// overlay was parsed from, not a rendering of them. That is the point of the +// templates being files: a contributor cropping in Photoshop against the +// download and one cropping in our form are working to the same frame, because +// there is only one artefact +func (l *Loader) Bytes(key string) ([]byte, bool) { + // Looked up in the parsed set first, so a key that names no template can + // never reach the filesystem path below + if _, ok := l.Template(key); !ok { + return nil, false + } + + data, err := defaultTemplates.ReadFile(path.Join(templateDir, key+TemplateExt)) + if err != nil { + return nil, false + } + return data, true +} diff --git a/internal/image/croptemplate/loader_test.go b/internal/image/croptemplate/loader_test.go new file mode 100644 index 000000000..d1ef5b58e --- /dev/null +++ b/internal/image/croptemplate/loader_test.go @@ -0,0 +1,71 @@ +package croptemplate + +import ( + "slices" + "testing" +) + +// The guarantee the whole design rests on: the file a contributor downloads +// parses to the frame the edit form draws. If these ever diverge, two people +// cropping the same image to the same type get different frames +func TestLoaderBytesParseToTheTemplateServed(t *testing.T) { + loader := NewLoader() + + for _, key := range loaderKeys(t) { + t.Run(key, func(t *testing.T) { + data, ok := loader.Bytes(key) + if !ok { + t.Fatal("no bytes") + } + downloaded, err := Parse(data) + if err != nil { + t.Fatalf("the served file does not parse: %v", err) + } + + shown, ok := loader.Template(key) + if !ok { + t.Fatal("no template") + } + + if downloaded.Width != shown.Width || downloaded.Height != shown.Height { + t.Errorf("download is %dx%d but the overlay is %dx%d", + downloaded.Width, downloaded.Height, shown.Width, shown.Height) + } + if !slices.Equal(downloaded.Guides, shown.Guides) { + t.Errorf("download guides %+v, overlay %+v", downloaded.Guides, shown.Guides) + } + }) + } +} + +// Keys reach Bytes from a URL. They are resolved against the parsed set rather +// than joined onto a path, so a traversal cannot name a file - this asserts +// that guard stays. +func TestLoaderBytesRefuseKeysThatNameNoTemplate(t *testing.T) { + loader := NewLoader() + + for _, key := range []string{"", "..", "../secret", "/etc/passwd", "CROP_FACE/../../secret", "nope"} { + t.Run(key, func(t *testing.T) { + if _, ok := loader.Bytes(key); ok { + t.Errorf("key %q served bytes", key) + } + }) + } +} + +func loaderKeys(t *testing.T) []string { + t.Helper() + defaults, err := Defaults() + if err != nil { + t.Fatalf("Defaults: %v", err) + } + if len(defaults) == 0 { + t.Fatal("no templates are shipped") + } + keys := make([]string, 0, len(defaults)) + for key := range defaults { + keys = append(keys, key) + } + slices.Sort(keys) + return keys +} diff --git a/internal/image/croptemplate/psd.go b/internal/image/croptemplate/psd.go new file mode 100644 index 000000000..7c6ffe8a4 --- /dev/null +++ b/internal/image/croptemplate/psd.go @@ -0,0 +1,277 @@ +// Package croptemplate reads crop guide geometry out of Photoshop template +// files +// +// The .psd files are the source of truth for the crop overlay, rather than a +// table in Go that a writer turns into downloadable files. The bytes a +// contributor downloads are the bytes the frame in the edit form was parsed +// from, so the two cannot drift +package croptemplate + +import ( + "encoding/binary" + "errors" + "fmt" + "sort" +) + +// ErrNoGuides reports a structurally valid PSD carrying no guides. Separate +// from a parse failure because the file is fine and the mistake in it is a +// specific one: guides were drawn as lines rather than dragged off a ruler, or +// flattened away on export +var ErrNoGuides = errors.New("psd contains no guides") + +// Axis is a guide's orientation, which also decides what its position is a +// fraction of +type Axis string + +const ( + AxisX Axis = "X" + AxisY Axis = "Y" +) + +// Guide is one line of a template +type Guide struct { + Axis Axis + // Position is a fraction of the canvas along Axis: 0 is the left or top + // edge, 1 the right or bottom. Fractions rather than pixels because a + // template is drawn at one size and rendered at every other + Position float64 + + // Role and Label come from the template's XMP, and are empty when it + // carries none. A template with guides and no annotation is a usable + // overlay, just an unlabelled one, so neither is required + Role Role + Label string + + // Pivot marks the line a frame resizes about when Shift is held, and comes + // from the XMP like the rest. Not derivable from Role: Role says how closely + // a line is meant to be followed, this says what the frame turns about, and + // in the face template the eye line is the softest line and also the right + // one to resize around. At most one per axis; none means the centre + Pivot bool +} + +// Template is the geometry of one crop +type Template struct { + Width int + Height int + Guides []Guide + + // Shapes are outlines drawn on the template's own layers like an oval for a + // face to sit inside, a bar marking a margin. Guidance only: the crop is + // still a rectangle + Shapes []Shape +} + +// AspectRatio is width over height, taken from the canvas rather than +// configured anywhere +func (t Template) AspectRatio() float64 { + return float64(t.Width) / float64(t.Height) +} + +// Layout of the guide payload, which container.go's walk delivers here whole +// +// Every number here is quoted from the Adobe Photoshop File Formats +// Specification, which is the only authority on any of it: +// +// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ +// +// The section names below are that document's own headings. Naming them is +// worth the lines: checking whether a constant is right is otherwise a day of +// counting bytes against a hex dump, and a wrong one does not fail - it reads +// a plausible number out of the wrong offset and draws a shape nobody made +const ( + // "Image Resource IDs": 1032 is "(Photoshop 4.0) Grid and guides + // information". Its payload, per "Grid and guides resource format", is a + // version, the grid spacing, a guide count, then one fixed-size record per + // guide -- position and axis, and nothing else. Labels cannot live here; + // they come from the XMP resource instead. + resourceGuides = 1032 + + // The spec calls a guide's position "Location of guide in document + // coordinates" and does not give the unit. It is 1/32 of a pixel: that is + // what every other implementation assumes, and what this corpus confirms + // -- CROP_FACE's eye line reads back at exactly 508.0625 px on a 1280-high + // canvas, which is 16258 units and nothing else. + guideUnitsPerPixel = 32 + + // "Grid and guides resource format", per guide: 4 bytes of location and + // 1 byte of direction. + guideRecordLen = 5 + + // The same section, before the records: 4 bytes of version, 8 of grid + // cycle (horizontal then vertical), and 4 of guide count. + guideHeaderLen = 16 + + // Direction, same section: 0 is a vertical guide, 1 horizontal. A vertical + // line is positioned across the width, which is AxisX here -- the two + // vocabularies name it from different ends. + axisVertical = 0 +) + +// Parse reads a template's geometry from PSD bytes +// +// Guides come back sorted by axis and then position. Photoshop stores them in +// the order they were created, which makes otherwise identical templates +// compare unequal and test failures hard to read +func Parse(data []byte) (Template, error) { + // No pixel is ever decoded. A template is geometry, and a shape layer's + // raster is several megabytes of something nothing here looks at. + file, err := decode(data) + if err != nil { + return Template{}, fmt.Errorf("reading PSD: %w", err) + } + + width, height := file.width, file.height + if width <= 0 || height <= 0 { + return Template{}, fmt.Errorf("PSD has empty canvas: %dx%d", width, height) + } + + var guides []Guide + if block, ok := file.res[resourceGuides]; ok { + if guides, err = parseGuides(block, width, height); err != nil { + return Template{}, fmt.Errorf("reading guides: %w", err) + } + } + + // Outlines stay outlines. A line drawn as a hairline bar on a shape layer + // is a guide in intent, but only a ruler guide carries a role and a label, + // and nothing positional can recover those: in this corpus the margin + // convention and the topmost anchor sit a hundredth of a canvas apart, so + // any threshold that separated them would swap the other pair over. That + // conversion belongs to whoever prepares the template, where the answer is + // known rather than guessed + shapes := collectShapes(file.layers) + + // Either kind of geometry makes a usable template: one whose whole content + // is an oval for a face has no ruler guides at all + if len(guides) == 0 && len(shapes) == 0 { + return Template{}, ErrNoGuides + } + + sort.Slice(guides, func(a, b int) bool { + if guides[a].Axis != guides[b].Axis { + return guides[a].Axis < guides[b].Axis + } + return guides[a].Position < guides[b].Position + }) + + // Annotations are optional and their absence is not an error, so a missing + // or unreadable XMP packet costs the labels and nothing else + if packet, ok := file.res[resourceXMP]; ok { + guides = annotate(guides, parseAnnotations(packet)) + } + + return Template{Width: width, Height: height, Guides: guides, Shapes: shapes}, nil +} + +func parseGuides(block []byte, width, height int) ([]Guide, error) { + r := &reader{buf: block} + + r.skip(4) // version + r.skip(4) // grid cycle, horizontal + r.skip(4) // grid cycle, vertical + count := int(r.uint32()) + + if r.err != nil { + return nil, r.err + } + + // The count is a length field in a file we did not write, so it is checked + // against what the block can actually hold before it is used to size + // anything. Divided rather than multiplied: the product overflows a 32-bit + // int well before the comparison could catch it + if count < 0 || count > (len(block)-guideHeaderLen)/guideRecordLen { + return nil, fmt.Errorf("guide count %d exceeds block of %d bytes", count, len(block)) + } + + guides := make([]Guide, 0, count) + for range count { + location := r.uint32() + axis := AxisY + span := height + if r.uint8() == axisVertical { + axis = AxisX + span = width + } + if r.err != nil { + return nil, r.err + } + + // A guide dragged off the canvas and left there is stored as it was + // placed, and would reach the client as a Float of 167772.11. Refused + // rather than clamped: a line outside the picture is a template someone + // needs to look at, and pinning it to the edge would hide that while + // drawing something the designer never placed + position := float64(location) / guideUnitsPerPixel / float64(span) + if position < 0 || position > 1 { + return nil, fmt.Errorf("guide at %.3f is outside the canvas", position) + } + + guides = append(guides, Guide{Axis: axis, Position: position}) + } + + return guides, nil +} + +// reader walks a byte slice, refusing to read past the end +// +// Every length in the format is treated as untrusted regardless of where the +// file came from: a field can claim more bytes than the file holds, and +// slicing on it directly would panic inside a GraphQL resolver. The error is +// sticky, so a caller can read a run of fields and check once at the end. +type reader struct { + buf []byte + pos int + err error +} + +func (r *reader) remaining() int { + if r.err != nil { + return 0 + } + return len(r.buf) - r.pos +} + +// take advances by n, reporting whether the read was in bounds. A negative n +// is possible when a length field is read as a signed int, and is a truncation +// rather than a rewind +func (r *reader) take(n int) []byte { + if r.err != nil { + return nil + } + if n < 0 || n > len(r.buf)-r.pos { + r.err = fmt.Errorf("truncated at byte %d: wanted %d bytes, %d remain", r.pos, n, len(r.buf)-r.pos) + return nil + } + out := r.buf[r.pos : r.pos+n] + r.pos += n + return out +} + +func (r *reader) skip(n int) { r.take(n) } +func (r *reader) bytes(n int) []byte { return r.take(n) } + +func (r *reader) uint8() uint8 { + b := r.take(1) + if b == nil { + return 0 + } + return b[0] +} + +func (r *reader) uint16() uint16 { + b := r.take(2) + if b == nil { + return 0 + } + return binary.BigEndian.Uint16(b) +} + +func (r *reader) uint32() uint32 { + b := r.take(4) + if b == nil { + return 0 + } + return binary.BigEndian.Uint32(b) +} diff --git a/internal/image/croptemplate/psd_test.go b/internal/image/croptemplate/psd_test.go new file mode 100644 index 000000000..6300432e9 --- /dev/null +++ b/internal/image/croptemplate/psd_test.go @@ -0,0 +1,324 @@ +package croptemplate + +import ( + "encoding/binary" + "errors" + "math" + "testing" +) + +// Fixtures are assembled here rather than committed as files. A binary +// fixture cannot be reviewed, and one built by hand would only prove the +// parser agrees with whatever this package already believes. The committed +// templates are checked against their documented percentages separately, which +// is where a real Photoshop file earns its place in the tests. +// +// Everything the file format dictates -- the signatures, the guides resource +// ID, the 1/32 px fixed point -- is written out as a literal below rather than +// taken from the constants in psd.go. A fixture built from the constant it is +// meant to be checking agrees with any value of it, so the mutation that +// matters most, getting one of these numbers wrong, would survive. + +type rawGuide struct { + // location is in 1/32 px, as stored. + location uint32 + axis byte +} + +const ( + rawVertical = 0 + rawHorizontal = 1 +) + +// px converts pixels to the 1/32 px units a guide is stored in. +func px(n int) uint32 { return uint32(n) * 32 } + +// guidesResourceID is "Grid and guides information". +const guidesResourceID = 1032 + +// faceGuides is the real geometry of the corpus Face template on an 800x1200 +// canvas: quarters across, and hair, eye line and chin down. Stored in +// creation order, which is not sorted -- that is the point of one of the tests +// below. +var faceGuides = []rawGuide{ + {px(400), rawVertical}, + {px(600), rawVertical}, + {px(200), rawVertical}, + {px(30), rawHorizontal}, + {px(924), rawHorizontal}, + {px(510), rawHorizontal}, + {px(12), rawVertical}, + {px(788), rawVertical}, + {px(1188), rawHorizontal}, +} + +func guidesBlock(guides []rawGuide) []byte { + var b []byte + b = binary.BigEndian.AppendUint32(b, 1) // version + b = binary.BigEndian.AppendUint32(b, 576) // grid cycle, horizontal + b = binary.BigEndian.AppendUint32(b, 576) // grid cycle, vertical + b = binary.BigEndian.AppendUint32(b, uint32(len(guides))) + for _, g := range guides { + b = binary.BigEndian.AppendUint32(b, g.location) + b = append(b, g.axis) + } + return b +} + +// resourceBlock wraps a payload with an empty Pascal name, which is what +// Photoshop writes for every block in the corpus templates. +func resourceBlock(id uint16, data []byte) []byte { + var b []byte + b = append(b, "8BIM"...) + b = binary.BigEndian.AppendUint16(b, id) + b = append(b, 0, 0) // zero-length name, padded to an even length + b = binary.BigEndian.AppendUint32(b, uint32(len(data))) + b = append(b, data...) + if len(data)%2 != 0 { + b = append(b, 0) + } + return b +} + +func buildPSD(width, height int, resources []byte) []byte { + var b []byte + b = append(b, "8BPS"...) + b = binary.BigEndian.AppendUint16(b, 1) // 2 would be PSB + b = append(b, make([]byte, 6)...) // reserved + b = binary.BigEndian.AppendUint16(b, 3) // channels + b = binary.BigEndian.AppendUint32(b, uint32(height)) + b = binary.BigEndian.AppendUint32(b, uint32(width)) + b = binary.BigEndian.AppendUint16(b, 8) // bit depth + b = binary.BigEndian.AppendUint16(b, 3) // colour mode: RGB + b = binary.BigEndian.AppendUint32(b, 0) // colour mode data, empty + b = binary.BigEndian.AppendUint32(b, uint32(len(resources))) + b = append(b, resources...) + b = binary.BigEndian.AppendUint32(b, 0) // layer and mask info, empty + b = append(b, 0, 0) // image data, raw + return b +} + +func facePSD() []byte { + return buildPSD(800, 1200, resourceBlock(guidesResourceID, guidesBlock(faceGuides))) +} + +func assertGuides(t *testing.T, got []Guide, want []Guide) { + t.Helper() + + if len(got) != len(want) { + t.Fatalf("got %d guides, want %d: %+v", len(got), len(want), got) + } + for i := range want { + if got[i].Axis != want[i].Axis { + t.Errorf("guide %d: axis %q, want %q", i, got[i].Axis, want[i].Axis) + } + // A tolerance because positions are a division, and the templates put + // thirds on a canvas that cannot hold them exactly. + if math.Abs(got[i].Position-want[i].Position) > 1e-9 { + t.Errorf("guide %d: position %v, want %v", i, got[i].Position, want[i].Position) + } + } +} + +func TestParseReadsGuidesAsFractions(t *testing.T) { + template, err := Parse(facePSD()) + if err != nil { + t.Fatalf("Parse: %v", err) + } + + if template.Width != 800 || template.Height != 1200 { + t.Errorf("canvas %dx%d, want 800x1200", template.Width, template.Height) + } + + // Sorted by axis, then position: the margins and quarters across, then the + // hair, eye line, chin and bottom margin down. + assertGuides(t, template.Guides, []Guide{ + {Axis: AxisX, Position: 12.0 / 800}, + {Axis: AxisX, Position: 0.25}, + {Axis: AxisX, Position: 0.50}, + {Axis: AxisX, Position: 0.75}, + {Axis: AxisX, Position: 788.0 / 800}, + {Axis: AxisY, Position: 0.025}, + {Axis: AxisY, Position: 0.425}, + {Axis: AxisY, Position: 0.77}, + {Axis: AxisY, Position: 0.99}, + }) +} + +// A vertical guide is a fraction of width and a horizontal one a fraction of +// height, so a non-square canvas is the only thing that catches the two being +// swapped. +func TestParseMeasuresEachAxisAgainstItsOwnSpan(t *testing.T) { + psd := buildPSD(800, 1200, resourceBlock(guidesResourceID, guidesBlock([]rawGuide{ + {px(400), rawVertical}, + {px(400), rawHorizontal}, + }))) + + template, err := Parse(psd) + if err != nil { + t.Fatalf("Parse: %v", err) + } + + assertGuides(t, template.Guides, []Guide{ + {Axis: AxisX, Position: 0.5}, // 400 of 800 + {Axis: AxisY, Position: 400.0 / 1200}, // the same pixel, a third of the way down + }) +} + +func TestAspectRatioComesFromTheCanvas(t *testing.T) { + for _, tc := range []struct { + name string + width, height int + want float64 + }{ + {"portrait", 800, 1200, 2.0 / 3.0}, + {"landscape", 1280, 720, 16.0 / 9.0}, + {"a swapped-in shape", 900, 1200, 3.0 / 4.0}, + } { + t.Run(tc.name, func(t *testing.T) { + // Guides placed for this canvas rather than the face template's, so + // the test says something about the aspect ratio and nothing about + // whether guides drawn for one shape fit another. + centre := []rawGuide{ + {px(tc.width / 2), rawVertical}, + {px(tc.height / 2), rawHorizontal}, + } + psd := buildPSD(tc.width, tc.height, resourceBlock(guidesResourceID, guidesBlock(centre))) + template, err := Parse(psd) + if err != nil { + t.Fatalf("Parse: %v", err) + } + if got := template.AspectRatio(); math.Abs(got-tc.want) > 1e-9 { + t.Errorf("aspect ratio %v, want %v", got, tc.want) + } + }) + } +} + +// Photoshop stores guides in creation order and writes blocks we have no +// interest in, including odd-length ones that shift every later block by a pad +// byte if the walk gets it wrong. +func TestParseWalksPastOtherResources(t *testing.T) { + var resources []byte + resources = append(resources, resourceBlock(1005, make([]byte, 16))...) // resolution info + resources = append(resources, resourceBlock(1024, []byte{0, 1, 2})...) // odd length, forces a pad + resources = append(resources, resourceBlock(guidesResourceID, guidesBlock(faceGuides))...) + resources = append(resources, resourceBlock(1039, make([]byte, 672))...) // ICC profile + + template, err := Parse(buildPSD(800, 1200, resources)) + if err != nil { + t.Fatalf("Parse: %v", err) + } + if len(template.Guides) != len(faceGuides) { + t.Fatalf("got %d guides, want %d", len(template.Guides), len(faceGuides)) + } +} + +func TestParseReportsMissingGuides(t *testing.T) { + t.Run("no guides block", func(t *testing.T) { + psd := buildPSD(800, 1200, resourceBlock(1005, make([]byte, 16))) + if _, err := Parse(psd); !errors.Is(err, ErrNoGuides) { + t.Errorf("got %v, want ErrNoGuides", err) + } + }) + + t.Run("empty guides block", func(t *testing.T) { + psd := buildPSD(800, 1200, resourceBlock(guidesResourceID, guidesBlock(nil))) + if _, err := Parse(psd); !errors.Is(err, ErrNoGuides) { + t.Errorf("got %v, want ErrNoGuides", err) + } + }) +} + +func TestParseRejectsMalformedFiles(t *testing.T) { + for _, tc := range []struct { + name string + psd []byte + }{ + {"empty", nil}, + {"not a PSD", []byte("GIF89a and then some padding to get past the header")}, + { + "PSB, whose section lengths are 64-bit", + func() []byte { + psd := facePSD() + binary.BigEndian.PutUint16(psd[4:], 2) + return psd + }(), + }, + { + "zero-height canvas", + buildPSD(800, 0, resourceBlock(guidesResourceID, guidesBlock(faceGuides))), + }, + { + "a resource length running past the file", + func() []byte { + resources := resourceBlock(guidesResourceID, guidesBlock(faceGuides)) + // The size field sits after the signature, ID and empty name. + binary.BigEndian.PutUint32(resources[8:], math.MaxUint32) + return buildPSD(800, 1200, resources) + }(), + }, + { + "garbage where a resource block should start", + buildPSD(800, 1200, []byte("not a block at all, but long enough to try")), + }, + } { + t.Run(tc.name, func(t *testing.T) { + if _, err := Parse(tc.psd); err == nil { + t.Error("got nil error, want a parse failure") + } + }) + } +} + +// A guide is stored where it was dragged, and Photoshop will happily leave one +// off the canvas. Divided by the canvas it becomes a position like 167772.11, +// which the resolver would hand to a client as a Float and the overlay would +// draw somewhere far outside the picture. +// +// Refused rather than clamped: a line outside the frame is a template someone +// needs to look at, and pinning it to the edge would hide that while drawing +// something the designer never placed. +func TestParseRefusesGuidesOutsideTheCanvas(t *testing.T) { + for _, tc := range []struct { + name string + guide rawGuide + }{ + {"past the right edge", rawGuide{px(801), rawVertical}}, + {"far past it", rawGuide{px(1 << 20), rawVertical}}, + {"below the bottom", rawGuide{px(1201), rawHorizontal}}, + } { + t.Run(tc.name, func(t *testing.T) { + psd := buildPSD(800, 1200, + resourceBlock(guidesResourceID, guidesBlock([]rawGuide{tc.guide}))) + + if _, err := Parse(psd); err == nil { + t.Error("accepted a guide outside the canvas") + } + }) + } + + // The edges themselves are where a crop box is drawn, and must stay legal. + psd := buildPSD(800, 1200, resourceBlock(guidesResourceID, guidesBlock([]rawGuide{ + {px(0), rawVertical}, + {px(800), rawVertical}, + {px(1200), rawHorizontal}, + }))) + if _, err := Parse(psd); err != nil { + t.Errorf("refused a guide on the edge: %v", err) + } +} + +// The count is multiplied by the record length to check it fits. On a 32-bit +// int that product wraps long before the comparison could catch it, so the +// check is written as a division instead. +func TestParseRefusesAnImpossibleGuideCount(t *testing.T) { + block := guidesBlock([]rawGuide{{px(400), rawVertical}}) + // Overwrite the count, which sits after the version and two grid cycles. + binary.BigEndian.PutUint32(block[12:16], 0x40000001) + + psd := buildPSD(800, 1200, resourceBlock(guidesResourceID, block)) + if _, err := Parse(psd); err == nil { + t.Error("accepted a guide count larger than the block can hold") + } +} diff --git a/internal/image/croptemplate/shapes.go b/internal/image/croptemplate/shapes.go new file mode 100644 index 000000000..e4c9b6d4e --- /dev/null +++ b/internal/image/croptemplate/shapes.go @@ -0,0 +1,196 @@ +package croptemplate + +// Point is a position on the canvas, as fractions of its width and height. +// +// Fractions all the way through, like guide positions, because a template is +// drawn at one size and rendered at every other. Photoshop already stores path +// points this way, so nothing has to be divided by the canvas on the way in. +type Point struct { + X float64 + Y float64 +} + +// Knot is one anchor of a path with the control points either side of it. +// +// Every segment is a cubic curve, including the straight ones -- Photoshop +// draws a straight edge as a curve whose controls sit on top of its anchors, +// and a rectangle and an ellipse come through here in exactly the same shape. +// Nothing tries to recover "this was an ellipse" from the knots: four cubics +// are what an ellipse is in this format, and an SVG path draws them directly. +type Knot struct { + In Point + Anchor Point + Out Point +} + +// Subpath is one continuous run of knots. +type Subpath struct { + Closed bool + Knots []Knot +} + +// Shape is one outline drawn in a template: an oval for a face to sit inside, +// a bar marking a margin, anything a designer can draw with the shape tool. +// +// Guidance, never a constraint. The crop is still a rectangle and the server +// still cuts one; a shape says where to put the subject within it. +type Shape struct { + // Label is the layer's name in the template. Photoshop puts it right there + // and a designer naming a layer "head guide" has already said what it is, + // so there is nothing to annotate separately. + Label string + Subpaths []Subpath +} + +// Additional layer information keys, from "Additional Layer Information" in +// the spec: "Vector mask setting" (vmsk) and "Vector Stroke Data" (vsms). +// +// Photoshop writes vsms for a shape layer's outline and vmsk for a plain +// vector mask. Despite the names, both carry the same payload -- a path +// resource, read by parsePath below -- and a file can carry either. +const ( + keyVectorMaskShape = "vsms" + keyVectorMask = "vmsk" +) + +// The vector-mask payload reuses the record layout of "Path resource format" +// in the Adobe Photoshop File Formats Specification -- the section documents +// the document-level path resources, but "Vector mask setting" defers to it +// for the records. See the reference at the top of psd.go's constants. +const ( + // "Photoshop stores the path information in a series of 26-byte path point + // records": a two-byte selector and twenty-four bytes of payload, whatever + // the selector turns out to mean. + pathRecordLen = 26 + + // The same section: knot coordinates are 8.24 fixed point, so a whole unit + // is 1 << 24. + fixedPointOne = 1 << 24 + + // The selector values, in the order the spec lists them. 6, 7 and 8 are + // fill rule and clipboard records, which say nothing about where a line + // goes and are skipped by the switch that reads these. + selClosedLength = 0 + selClosedLinked = 1 + selClosedUnlinked = 2 + selOpenLength = 3 + selOpenLinked = 4 + selOpenUnlinked = 5 +) + +// collectShapes reads the outlines out of a layer tree, in document order. +// +// Best-effort by design: a layer holding something unfamiliar is skipped rather +// than failing the template. The layer section is by far the largest and most +// variable part of a PSD -- adjustment layers, smart objects, effects, text -- +// and only the vector outlines in it are of any interest here. Refusing a +// template whose thirtieth layer holds something unusual would make the +// format's complexity somebody else's problem to work around. +func collectShapes(layers []psdLayer) []Shape { + var shapes []Shape + + for _, layer := range layers { + // A hidden group hides what is inside it, which the layer's own flag + // does not say: Photoshop leaves a child's flag alone when its folder + // is switched off. + if !layer.visible() { + continue + } + + if path, ok := outlineOf(layer); ok { + if subpaths := parsePath(path); len(subpaths) > 0 { + shapes = append(shapes, Shape{Label: layer.name, Subpaths: subpaths}) + } + } + + shapes = append(shapes, collectShapes(layer.children)...) + } + + return shapes +} + +// outlineOf returns a layer's vector path, whichever key it was written under. +func outlineOf(layer psdLayer) ([]byte, bool) { + for _, key := range []string{keyVectorMaskShape, keyVectorMask} { + if path, ok := layer.info[key]; ok && len(path) > 0 { + return path, true + } + } + return nil, false +} + +// parsePath reads the fixed-size records of a vector mask. +// +// The layout is a version and flags, then a run of 26-byte records. A length +// record opens a subpath and says how many knots follow; the knot records carry +// the geometry; fill rule and clipboard records are Photoshop's business and +// are stepped over. +func parsePath(body []byte) []Subpath { + r := &reader{buf: body} + r.skip(4) // version + r.skip(4) // flags + + var ( + subpaths []Subpath + current *Subpath + ) + + for r.remaining() >= pathRecordLen { + selector := r.uint16() + record := r.bytes(24) + if r.err != nil { + break + } + + switch selector { + case selClosedLength, selOpenLength: + // A trailing length record with no knots after it describes an + // empty subpath, which is nothing to draw. + if current != nil && len(current.Knots) > 0 { + subpaths = append(subpaths, *current) + } + current = &Subpath{Closed: selector == selClosedLength} + + case selClosedLinked, selClosedUnlinked, selOpenLinked, selOpenUnlinked: + // A knot before any length record is malformed. Dropping it rather + // than inventing a subpath keeps a partly-written file from drawing + // a shape its author never made. + if current == nil { + continue + } + current.Knots = append(current.Knots, Knot{ + In: point(record[0:8]), + Anchor: point(record[8:16]), + Out: point(record[16:24]), + }) + } + } + + if current != nil && len(current.Knots) > 0 { + subpaths = append(subpaths, *current) + } + + return subpaths +} + +// point reads one path coordinate pair. +// +// Vertical before horizontal, which is the opposite of every other coordinate +// in the format and the easiest thing here to get quietly wrong: swapping them +// still parses, and still draws a shape, just the wrong one. +func point(b []byte) Point { + return Point{ + Y: fixedPoint(b[0:4]), + X: fixedPoint(b[4:8]), + } +} + +// fixedPoint reads a signed 8.24 fixed-point number. +// +// Already a fraction of the canvas, which is why these are the only lengths in +// the file that need no division. Values outside 0..1 are legitimate: a shape +// may hang over the edge, and a template's crop box is often drawn a hair +// outside the canvas so its stroke does not eat into the picture. +func fixedPoint(b []byte) float64 { + return float64(int32(uint32(b[0])<<24|uint32(b[1])<<16|uint32(b[2])<<8|uint32(b[3]))) / fixedPointOne +} diff --git a/internal/image/croptemplate/shapes_test.go b/internal/image/croptemplate/shapes_test.go new file mode 100644 index 000000000..1758444c5 --- /dev/null +++ b/internal/image/croptemplate/shapes_test.go @@ -0,0 +1,425 @@ +package croptemplate + +import ( + "encoding/binary" + "errors" + "math" + "testing" +) + +// As in psd_test.go, everything the format dictates is written out as a literal +// rather than taken from the constants in shapes.go: the 26-byte record, the +// 8.24 divisor, the selectors, the keys. A fixture built from the constant it +// is meant to be checking agrees with any value of it, and getting one of these +// numbers wrong is the mutation that matters most. + +// fixed24 encodes a canvas fraction as the signed 8.24 fixed point a path +// coordinate is stored in. +func fixed24(v float64) uint32 { return uint32(int32(v * (1 << 24))) } + +// pathPoint writes one coordinate pair: vertical first, then horizontal, which +// is the opposite of every other coordinate in the format. +func pathPoint(x, y float64) []byte { + var b []byte + b = binary.BigEndian.AppendUint32(b, fixed24(y)) + b = binary.BigEndian.AppendUint32(b, fixed24(x)) + return b +} + +// pathRecord is always 26 bytes: a two-byte selector and twenty-four of +// payload, whatever the selector means. +func pathRecord(selector uint16, payload []byte) []byte { + b := binary.BigEndian.AppendUint16(nil, selector) + b = append(b, payload...) + if len(payload) < 24 { + b = append(b, make([]byte, 24-len(payload))...) + } + return b[:26] +} + +type testKnot struct { + inX, inY float64 + anchorX, anchorY float64 + outX, outY float64 +} + +// corner is a knot whose controls sit on its anchor, which is how Photoshop +// stores a straight corner. A rectangle is four of these. +func corner(x, y float64) testKnot { + return testKnot{inX: x, inY: y, anchorX: x, anchorY: y, outX: x, outY: y} +} + +func knotRecord(selector uint16, k testKnot) []byte { + var payload []byte + payload = append(payload, pathPoint(k.inX, k.inY)...) + payload = append(payload, pathPoint(k.anchorX, k.anchorY)...) + payload = append(payload, pathPoint(k.outX, k.outY)...) + return pathRecord(selector, payload) +} + +// vectorMask builds a vsms payload: a version, flags, a subpath length record +// and then the knots. +func vectorMask(closed bool, knots []testKnot) []byte { + lengthSelector, knotSelector := uint16(0), uint16(1) + if !closed { + lengthSelector, knotSelector = 3, 4 + } + + b := binary.BigEndian.AppendUint32(nil, 3) // version + b = binary.BigEndian.AppendUint32(b, 0) // flags + + b = append(b, pathRecord(lengthSelector, + binary.BigEndian.AppendUint16(nil, uint16(len(knots))))...) + for _, k := range knots { + b = append(b, knotRecord(knotSelector, k)...) + } + return b +} + +// additionalBlock wraps a payload with the 8BIM signature and a four-character +// key, padded to an even length. +func additionalBlock(key string, data []byte) []byte { + b := append([]byte("8BIM"), key...) + b = binary.BigEndian.AppendUint32(b, uint32(len(data))) + b = append(b, data...) + if len(data)%2 != 0 { + b = append(b, 0) + } + return b +} + +// unicodeName builds a luni payload: a character count, then UTF-16. +func unicodeName(name string) []byte { + runes := []rune(name) + b := binary.BigEndian.AppendUint32(nil, uint32(len(runes))) + for _, r := range runes { + b = binary.BigEndian.AppendUint16(b, uint16(r)) + } + return b +} + +type testLayer struct { + name string + unicode string + hidden bool + vectorMask []byte +} + +func layerRecord(l testLayer) []byte { + var extra []byte + extra = binary.BigEndian.AppendUint32(extra, 0) // layer mask data, empty + extra = binary.BigEndian.AppendUint32(extra, 0) // blending ranges, empty + + // The legacy name is a Pascal string padded so it and its length byte + // occupy a multiple of four -- four here, not two as elsewhere. + extra = append(extra, byte(len(l.name))) + extra = append(extra, l.name...) + if pad := (len(l.name) + 1) % 4; pad != 0 { + extra = append(extra, make([]byte, 4-pad)...) + } + + if l.unicode != "" { + extra = append(extra, additionalBlock("luni", unicodeName(l.unicode))...) + } + if l.vectorMask != nil { + extra = append(extra, additionalBlock("vsms", l.vectorMask)...) + } + + var b []byte + b = append(b, make([]byte, 16)...) // bounds + b = binary.BigEndian.AppendUint16(b, 0) // channel count + b = append(b, "8BIM"...) + b = append(b, "norm"...) + b = append(b, 255) // opacity + b = append(b, 0) // clipping + if l.hidden { + b = append(b, 2) + } else { + b = append(b, 0) + } + b = append(b, 0) // filler + b = binary.BigEndian.AppendUint32(b, uint32(len(extra))) + b = append(b, extra...) + return b +} + +// buildPSDWithSection is buildPSD with a layer and mask section attached. +func buildPSDWithSection(width, height int, resources, section []byte) []byte { + var b []byte + b = append(b, "8BPS"...) + b = binary.BigEndian.AppendUint16(b, 1) + b = append(b, make([]byte, 6)...) + b = binary.BigEndian.AppendUint16(b, 3) + b = binary.BigEndian.AppendUint32(b, uint32(height)) + b = binary.BigEndian.AppendUint32(b, uint32(width)) + b = binary.BigEndian.AppendUint16(b, 8) + b = binary.BigEndian.AppendUint16(b, 3) + b = binary.BigEndian.AppendUint32(b, 0) + b = binary.BigEndian.AppendUint32(b, uint32(len(resources))) + b = append(b, resources...) + + b = binary.BigEndian.AppendUint32(b, uint32(len(section))) + b = append(b, section...) + b = append(b, 0, 0) + return b +} + +// shapePSD assembles a file whose only content is the given layers. +func shapePSD(layers ...testLayer) []byte { + info := binary.BigEndian.AppendUint16(nil, uint16(len(layers))) + for _, l := range layers { + info = append(info, layerRecord(l)...) + } + + // The layer info sub-section carries its own length before the count. + section := binary.BigEndian.AppendUint32(nil, uint32(len(info))) + section = append(section, info...) + + return buildPSDWithSection(800, 1200, nil, section) +} + +// An ellipse as Photoshop stores one: four knots, controls off to the sides. +func ellipseKnots() []testKnot { + return []testKnot{ + {inX: 0.28, inY: 0.02, anchorX: 0.5, anchorY: 0.02, outX: 0.72, outY: 0.02}, + {inX: 0.9, inY: 0.19, anchorX: 0.9, anchorY: 0.4, outX: 0.9, outY: 0.6}, + {inX: 0.72, inY: 0.77, anchorX: 0.5, anchorY: 0.77, outX: 0.28, outY: 0.77}, + {inX: 0.09, inY: 0.6, anchorX: 0.09, anchorY: 0.4, outX: 0.09, outY: 0.19}, + } +} + +func closeTo(t *testing.T, got, want float64, what string) { + t.Helper() + // A tolerance because 8.24 fixed point cannot hold most fractions exactly. + if math.Abs(got-want) > 1e-6 { + t.Errorf("%s: got %v, want %v", what, got, want) + } +} + +func TestParseReadsShapeOutlines(t *testing.T) { + template, err := Parse(shapePSD(testLayer{ + name: "head guide", + vectorMask: vectorMask(true, ellipseKnots()), + })) + if err != nil { + t.Fatalf("parsing: %v", err) + } + + if len(template.Shapes) != 1 { + t.Fatalf("got %d shapes, want 1", len(template.Shapes)) + } + shape := template.Shapes[0] + + if shape.Label != "head guide" { + t.Errorf("label %q, want %q", shape.Label, "head guide") + } + if len(shape.Subpaths) != 1 { + t.Fatalf("got %d subpaths, want 1", len(shape.Subpaths)) + } + if !shape.Subpaths[0].Closed { + t.Error("subpath should be closed") + } + if got := len(shape.Subpaths[0].Knots); got != 4 { + t.Fatalf("got %d knots, want 4", got) + } +} + +// The coordinate order is the easiest thing in this format to get quietly +// wrong: swapping the pair still parses and still draws a shape, just the wrong +// one. An asymmetric point is the only kind that can tell. +func TestParseReadsPointsVerticalFirst(t *testing.T) { + template, err := Parse(shapePSD(testLayer{ + name: "lopsided", + vectorMask: vectorMask(true, []testKnot{corner(0.25, 0.75), corner(0.5, 0.5)}), + })) + if err != nil { + t.Fatalf("parsing: %v", err) + } + + anchor := template.Shapes[0].Subpaths[0].Knots[0].Anchor + closeTo(t, anchor.X, 0.25, "anchor x") + closeTo(t, anchor.Y, 0.75, "anchor y") +} + +func TestParseKeepsControlPointsWithTheirAnchor(t *testing.T) { + template, err := Parse(shapePSD(testLayer{ + name: "curve", + vectorMask: vectorMask(true, ellipseKnots()), + })) + if err != nil { + t.Fatalf("parsing: %v", err) + } + + top := template.Shapes[0].Subpaths[0].Knots[0] + closeTo(t, top.Anchor.X, 0.5, "anchor x") + closeTo(t, top.In.X, 0.28, "incoming control x") + closeTo(t, top.Out.X, 0.72, "outgoing control x") +} + +// Points may sit outside the canvas: a crop box is usually drawn a hair +// outside it so its stroke does not eat into the picture. Clamping them would +// move the very lines a template exists to place. +func TestParseKeepsPointsOutsideTheCanvas(t *testing.T) { + template, err := Parse(shapePSD(testLayer{ + name: "crop box", + vectorMask: vectorMask(true, []testKnot{corner(-0.01, -0.02), corner(1.01, 1.02)}), + })) + if err != nil { + t.Fatalf("parsing: %v", err) + } + + first := template.Shapes[0].Subpaths[0].Knots[0].Anchor + closeTo(t, first.X, -0.01, "negative x") + closeTo(t, first.Y, -0.02, "negative y") +} + +func TestParseDistinguishesOpenSubpaths(t *testing.T) { + template, err := Parse(shapePSD(testLayer{ + name: "arc", + vectorMask: vectorMask(false, []testKnot{corner(0, 0), corner(1, 1)}), + })) + if err != nil { + t.Fatalf("parsing: %v", err) + } + + if template.Shapes[0].Subpaths[0].Closed { + t.Error("an open subpath should not come back closed") + } +} + +// A designer hides working geometry -- construction lines, alternates -- and +// drawing it over a contributor's photograph would show them something the +// template's author chose not to. +func TestParseSkipsHiddenLayers(t *testing.T) { + template, err := Parse(shapePSD( + testLayer{name: "shown", vectorMask: vectorMask(true, ellipseKnots())}, + testLayer{name: "working", hidden: true, vectorMask: vectorMask(true, ellipseKnots())}, + )) + if err != nil { + t.Fatalf("parsing: %v", err) + } + + if len(template.Shapes) != 1 { + t.Fatalf("got %d shapes, want 1", len(template.Shapes)) + } + if template.Shapes[0].Label != "shown" { + t.Errorf("kept %q, want the visible layer", template.Shapes[0].Label) + } +} + +// Most layers in a real template hold no outline at all, and that is not a +// failure -- it is a background, a text layer, a group divider. +func TestParseIgnoresLayersWithoutOutlines(t *testing.T) { + template, err := Parse(shapePSD( + testLayer{name: "Background"}, + testLayer{name: "oval", vectorMask: vectorMask(true, ellipseKnots())}, + testLayer{name: "Text"}, + )) + if err != nil { + t.Fatalf("parsing: %v", err) + } + + if len(template.Shapes) != 1 { + t.Fatalf("got %d shapes, want 1", len(template.Shapes)) + } +} + +// The Pascal name is legacy, single-byte and lossy. Photoshop writes both, and +// a template named in anything but ASCII is only correct in the Unicode one. +func TestParsePrefersTheUnicodeLayerName(t *testing.T) { + template, err := Parse(shapePSD(testLayer{ + name: "tete", + unicode: "tête", + vectorMask: vectorMask(true, ellipseKnots()), + })) + if err != nil { + t.Fatalf("parsing: %v", err) + } + + if got := template.Shapes[0].Label; got != "tête" { + t.Errorf("label %q, want %q", got, "tête") + } +} + +// A template whose whole content is an oval has no ruler guides at all, and +// refusing it would be refusing the shape feature outright. +func TestParseAcceptsATemplateWithOnlyShapes(t *testing.T) { + template, err := Parse(shapePSD(testLayer{ + name: "head guide", + vectorMask: vectorMask(true, ellipseKnots()), + })) + if err != nil { + t.Fatalf("a template with shapes and no guides should parse: %v", err) + } + if len(template.Guides) != 0 { + t.Errorf("got %d guides, want none", len(template.Guides)) + } + if len(template.Shapes) == 0 { + t.Error("shapes should have been read") + } +} + +// Neither kind of geometry is still nothing to draw. +func TestParseStillReportsATemplateWithNeither(t *testing.T) { + _, err := Parse(shapePSD(testLayer{name: "Background"})) + if !errors.Is(err, ErrNoGuides) { + t.Errorf("got %v, want ErrNoGuides", err) + } +} + +// A subpath declared and then left empty is nothing to draw, and emitting it +// would put a stroke cap on the picture like a speck of dust on the lens. +func TestParseIgnoresEmptySubpaths(t *testing.T) { + _, err := Parse(shapePSD(testLayer{ + name: "empty outline", + vectorMask: vectorMask(true, nil), + })) + if !errors.Is(err, ErrNoGuides) { + t.Errorf("got %v, want ErrNoGuides: an empty subpath is not a shape", err) + } +} + +// bar builds the hairline rectangle a designer draws when marking a line on a +// shape layer rather than dragging one off a ruler. +func bar(axis Axis, position, thickness float64) []testKnot { + if axis == AxisY { + return []testKnot{ + corner(0, position-thickness/2), corner(1, position-thickness/2), + corner(1, position+thickness/2), corner(0, position+thickness/2), + } + } + return []testKnot{ + corner(position-thickness/2, 0), corner(position-thickness/2, 1), + corner(position+thickness/2, 1), corner(position+thickness/2, 0), + } +} + +// A bar spanning the picture is a line in intent, and this deliberately does +// not read it as one. +// +// Only a ruler guide carries a role and a label, and no measurement recovers +// them: across the shipped templates the margin convention sits at 0.015 and +// the topmost anchor at 0.010, so a threshold placed to tell those apart puts +// 44 of the 47 roled guides in the wrong category, and REFERENCE has no bar +// form at all. Converting where the answer is known -- in whoever prepares the +// template -- is the only version of this that can be right. +func TestParseLeavesBarsAsShapes(t *testing.T) { + template, err := Parse(shapePSD(testLayer{ + name: "eyes", + vectorMask: vectorMask(true, bar(AxisY, 0.425, 0.0008)), + })) + if err != nil { + t.Fatalf("parsing: %v", err) + } + + if len(template.Guides) != 0 { + t.Errorf("got %d guides, want none: a bar is an outline like any other", + len(template.Guides)) + } + if len(template.Shapes) != 1 { + t.Fatalf("got %d shapes, want 1", len(template.Shapes)) + } + if template.Shapes[0].Label != "eyes" { + t.Errorf("got label %q, want %q", template.Shapes[0].Label, "eyes") + } +} diff --git a/internal/image/croptemplate/templates/CROP_BUST.psd b/internal/image/croptemplate/templates/CROP_BUST.psd new file mode 100644 index 000000000..9023ca19d Binary files /dev/null and b/internal/image/croptemplate/templates/CROP_BUST.psd differ diff --git a/internal/image/croptemplate/templates/CROP_FACE.psd b/internal/image/croptemplate/templates/CROP_FACE.psd new file mode 100644 index 000000000..35d0b026a Binary files /dev/null and b/internal/image/croptemplate/templates/CROP_FACE.psd differ diff --git a/internal/image/croptemplate/templates/CROP_FULL_BODY.psd b/internal/image/croptemplate/templates/CROP_FULL_BODY.psd new file mode 100644 index 000000000..9275bbe90 Binary files /dev/null and b/internal/image/croptemplate/templates/CROP_FULL_BODY.psd differ diff --git a/internal/image/croptemplate/templates/CROP_THREE_QUARTER.psd b/internal/image/croptemplate/templates/CROP_THREE_QUARTER.psd new file mode 100644 index 000000000..324e4c438 Binary files /dev/null and b/internal/image/croptemplate/templates/CROP_THREE_QUARTER.psd differ diff --git a/internal/image/croptemplate/templates/CROP_THREE_QUARTER_PLUS.psd b/internal/image/croptemplate/templates/CROP_THREE_QUARTER_PLUS.psd new file mode 100644 index 000000000..81a8f65d9 Binary files /dev/null and b/internal/image/croptemplate/templates/CROP_THREE_QUARTER_PLUS.psd differ diff --git a/internal/image/croptemplate/templates/CROP_TORSO.psd b/internal/image/croptemplate/templates/CROP_TORSO.psd new file mode 100644 index 000000000..d7781a4bf Binary files /dev/null and b/internal/image/croptemplate/templates/CROP_TORSO.psd differ diff --git a/internal/image/croptemplate/templates/CROP_WIDE.psd b/internal/image/croptemplate/templates/CROP_WIDE.psd new file mode 100644 index 000000000..9ca5ac7b1 Binary files /dev/null and b/internal/image/croptemplate/templates/CROP_WIDE.psd differ diff --git a/internal/image/croptemplate/templates/README.md b/internal/image/croptemplate/templates/README.md new file mode 100644 index 000000000..0b87f89be --- /dev/null +++ b/internal/image/croptemplate/templates/README.md @@ -0,0 +1,41 @@ +# Built-in crop templates + +One `.psd` per Crop type, named for its image type key, embedded in the binary +so the feature needs no setup. + +`CROP_FACE.psd` is the only template carrying a shape outline rather than +lines alone; it is why the shape path exists at all. + +| File | Notes | +| ----------------------------- | -------------------------------------------------- | +| `CROP_FACE.psd` | 853x1280, and the only one with a shape | +| `CROP_BUST.psd` | | +| `CROP_TORSO.psd` | same geometry as Three-quarter, different meanings | +| `CROP_THREE_QUARTER.psd` | | +| `CROP_THREE_QUARTER_PLUS.psd` | | +| `CROP_FULL_BODY.psd` | margins only; no thirds | +| `CROP_WIDE.psd` | the only 16:9 template | + +Torso and Three-quarter share their geometry -- the same lines at the same +places -- and are separate files because those lines mean different things. The +files differ only in their labels. + +## Labels + +Each file carries an XMP packet naming its guides -- "bisects the eyes", +"where the thighs meet" -- so one file is the whole template and a downloaded +copy cannot arrive separated from its annotations. + +stash-box only **reads** that packet; authoring it is out of scope for this +application. Labels are optional -- a template with guides and no XMP is a +working overlay, just an unlabelled one. + +## Changing one + +Drop the replacement in under the same name. Nothing in the code knows where a +line is meant to sit, so there is nothing else to update -- the tests check +that whatever ships parses and is usable, not that it matches a table someone +typed. + +If the replacement brings its own XMP, it is already annotated. If not, it +still works, just without the label text. diff --git a/internal/image/croptemplate/xmp.go b/internal/image/croptemplate/xmp.go new file mode 100644 index 000000000..4d9671f79 --- /dev/null +++ b/internal/image/croptemplate/xmp.go @@ -0,0 +1,258 @@ +package croptemplate + +import ( + "bytes" + "encoding/xml" + "math" + "strconv" + "strings" +) + +// Namespace is the XMP vocabulary carrying guide annotations +// +// Block 1032 has room for a position and an axis and nothing else, so a guide +// cannot say it is the eye line, or that it is an anchor to be hit rather than +// a reference to be judged against. That distinction is most of what makes an +// overlay teach instead of decorate, so it rides along in the XMP packet +// instead of in a sidecar file: one file still holds the whole template, and +// what a contributor downloads cannot arrive separated from its labels. +const Namespace = "https://stashapp.github.io/stash-box/ns/crop-template/1.0/" + +const ( + // "Image Resource IDs": 1060 is "(Photoshop 7.0) XMP metadata. File info as + // XML description." See the spec reference at the top of psd.go's + // constants. The payload is an XMP packet and nothing here is + // Photoshop-specific -- what is read out of it is our own vocabulary, + // declared at Namespace above. + resourceXMP = 1060 + + // positionTolerance is how far an annotation may sit from the guide it + // describes. + // + // Generous on purpose. Guides land on whole pixels, so a template drawn at + // 800 px wide puts its thirds at 33.25% rather than 33.333%, and an author + // writing the round number should still match. The smallest gap between + // two guides in any of the templates is around twenty percentage points, + // so there is no risk of an annotation reaching the wrong line. + positionTolerance = 0.005 +) + +// Role is how closely a guide is meant to be followed. The corpus draws this +// distinction in prose -- some lines "act as anchors that should be used with +// some precision", others are "merely intended for additional reference" -- +// and it is worth keeping, because it is the difference between a rule and a +// suggestion. +type Role string + +const ( + RoleAnchor Role = "ANCHOR" + RoleReference Role = "REFERENCE" + RoleMargin Role = "MARGIN" +) + +// annotation is one guide's description, before it is matched to a guide. +type annotation struct { + Axis Axis + Position float64 + Role Role + Label string + Pivot bool +} + +// annotate attaches labels to guides, matching on axis and position. +// +// Geometry stays the authority: block 1032 says where the lines are and XMP +// only names them. That ordering is what stops the two disagreeing about +// anything that matters -- an annotation matching no guide is dropped rather +// than conjuring a line the template does not have, and a guide matching no +// annotation simply goes unlabelled. +func annotate(guides []Guide, annotations []annotation) []Guide { + for i, guide := range guides { + for _, a := range annotations { + // An entry carrying nothing is not naming anything, so it does not + // get to consume the match: a typo'd role with a blank label would + // otherwise take the guide and leave a better entry at the same + // position unread. A pivot counts as something to say, or an entry + // marking only that would be discarded as empty. + if a.Role == "" && a.Label == "" && !a.Pivot { + continue + } + if a.Axis == guide.Axis && math.Abs(a.Position-guide.Position) <= positionTolerance { + guides[i].Role = a.Role + guides[i].Label = a.Label + guides[i].Pivot = a.Pivot + break + } + } + } + return dropAmbiguousPivots(guides) +} + +// parseAnnotations reads guide descriptions out of an XMP packet. +// +// Every failure here is silent, and deliberately so: the packet is mostly +// written by other software and full of vocabularies that are none of our +// business, so anything unreadable means an unannotated template rather than a +// broken one. Geometry is the contract; labels are a bonus, and losing them +// must never cost an instance its templates. +func parseAnnotations(packet []byte) []annotation { + decoder := xml.NewDecoder(bytes.NewReader(packet)) + + // Photoshop and Adobe's toolkit disagree with each other about how deeply + // rdf:Description is nested, so the element is searched for by name rather + // than reached by a path. + for { + // Any error, including a clean EOF, means there is nothing of ours in + // the packet. + token, err := decoder.Token() + if err != nil { + return nil + } + + start, ok := token.(xml.StartElement) + if !ok || start.Name.Space != Namespace || start.Name.Local != "guides" { + continue + } + + var parsed xmpGuides + if err := decoder.DecodeElement(&parsed, &start); err != nil { + return nil + } + return parsed.annotations() + } +} + +// The rdf:Seq is a nested struct rather than an "a>b" path in the tag, because +// that shorthand does not carry namespaces through each segment and silently +// matches nothing here. +type xmpGuides struct { + Seq xmpSeq `xml:"http://www.w3.org/1999/02/22-rdf-syntax-ns# Seq"` +} + +type xmpSeq struct { + Items []xmpGuide `xml:"http://www.w3.org/1999/02/22-rdf-syntax-ns# li"` +} + +// xmpGuide accepts both the shorthand form, where a struct's fields are +// attributes, and the expanded form, where they are child elements. +// +// Both are legal XMP and we do not control which one survives: Adobe's toolkit +// normalises shorthand to the expanded form when it rewrites a packet, so a +// template that round-trips through Photoshop can come back in the other +// shape. Reading only one of them would lose every label the first time a +// designer re-saved a file. +type xmpGuide struct { + AxisAttr string `xml:"https://stashapp.github.io/stash-box/ns/crop-template/1.0/ axis,attr"` + PositionAttr string `xml:"https://stashapp.github.io/stash-box/ns/crop-template/1.0/ position,attr"` + RoleAttr string `xml:"https://stashapp.github.io/stash-box/ns/crop-template/1.0/ role,attr"` + LabelAttr string `xml:"https://stashapp.github.io/stash-box/ns/crop-template/1.0/ label,attr"` + PivotAttr string `xml:"https://stashapp.github.io/stash-box/ns/crop-template/1.0/ pivot,attr"` + + AxisElem string `xml:"https://stashapp.github.io/stash-box/ns/crop-template/1.0/ axis"` + PositionElem string `xml:"https://stashapp.github.io/stash-box/ns/crop-template/1.0/ position"` + RoleElem string `xml:"https://stashapp.github.io/stash-box/ns/crop-template/1.0/ role"` + LabelElem string `xml:"https://stashapp.github.io/stash-box/ns/crop-template/1.0/ label"` + PivotElem string `xml:"https://stashapp.github.io/stash-box/ns/crop-template/1.0/ pivot"` +} + +func (g xmpGuides) annotations() []annotation { + out := make([]annotation, 0, len(g.Seq.Items)) + + for _, item := range g.Seq.Items { + axis := parseAxis(pick(item.AxisElem, item.AxisAttr)) + if axis == "" { + continue + } + position, err := strconv.ParseFloat(strings.TrimSpace(pick(item.PositionElem, item.PositionAttr)), 64) + if err != nil { + continue + } + + out = append(out, annotation{ + Axis: axis, + Position: position, + // An unrecognised role leaves the guide unroled but keeps its + // label. A typo should cost the distinction it got wrong, not the + // name of the line. + Role: parseRole(pick(item.RoleElem, item.RoleAttr)), + Label: strings.TrimSpace(pick(item.LabelElem, item.LabelAttr)), + Pivot: parseBool(pick(item.PivotElem, item.PivotAttr)), + }) + } + + return out +} + +// pick prefers the expanded form, which is what a packet normalised by Adobe's +// toolkit will carry. +func pick(elem, attr string) string { + if strings.TrimSpace(elem) != "" { + return elem + } + return attr +} + +// parseBool reads the flag forms an XMP packet actually carries. "True" is +// what Adobe's toolkit writes for a boolean; "1" is what a hand-written packet +// is likely to say. Anything else, a typo included, leaves the flag unset -- a +// guide that cannot say whether it is the pivot is not the pivot. +func parseBool(s string) bool { + switch strings.ToLower(strings.TrimSpace(s)) { + case "true", "1", "yes": + return true + default: + return false + } +} + +// dropAmbiguousPivots clears the pivot from any axis claiming more than one. +// +// A frame cannot be held still at two points on an axis and still have +// anything left for the drag to change, so a template saying so has said +// nothing usable. Cleared rather than resolved by taking the first: the order +// guides arrive in is the order block 1032 happens to store them, and picking +// by it would make the behaviour depend on something no author can see. +// +// Dropping both leaves the axis resizing about its centre, which is what a +// template with no pivot does anyway -- a defined answer rather than an +// arbitrary one. The tool that writes these refuses the pair outright; this is +// for the templates it did not write. +func dropAmbiguousPivots(guides []Guide) []Guide { + claims := make(map[Axis]int, 2) + for _, guide := range guides { + if guide.Pivot { + claims[guide.Axis]++ + } + } + + for i, guide := range guides { + if guide.Pivot && claims[guide.Axis] > 1 { + guides[i].Pivot = false + } + } + return guides +} + +func parseAxis(s string) Axis { + switch strings.ToUpper(strings.TrimSpace(s)) { + case "X": + return AxisX + case "Y": + return AxisY + default: + return "" + } +} + +func parseRole(s string) Role { + switch strings.ToUpper(strings.TrimSpace(s)) { + case "ANCHOR": + return RoleAnchor + case "REFERENCE": + return RoleReference + case "MARGIN": + return RoleMargin + default: + return "" + } +} diff --git a/internal/image/croptemplate/xmp_test.go b/internal/image/croptemplate/xmp_test.go new file mode 100644 index 000000000..4fc160e80 --- /dev/null +++ b/internal/image/croptemplate/xmp_test.go @@ -0,0 +1,525 @@ +package croptemplate + +import ( + "fmt" + "strings" + "testing" +) + +// The namespace is written out here rather than taken from the constant, for +// the same reason the format literals in psd_test.go are: a fixture that reads +// its namespace from the code it is checking agrees with any namespace. +const testNamespace = "https://stashapp.github.io/stash-box/ns/crop-template/1.0/" + +// xmpPacket wraps guide entries in the envelope Photoshop writes, so the +// element is found at the depth it really appears at rather than at the root. +func xmpPacket(entries string) []byte { + return []byte(fmt.Sprintf(`<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> +<x:xmpmeta xmlns:x="adobe:ns:meta/"> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> + <rdf:Description rdf:about="" + xmlns:tiff="http://ns.adobe.com/tiff/1.0/" + xmlns:sbox="%s"> + <tiff:Orientation>1</tiff:Orientation> + <sbox:guides> + <rdf:Seq> +%s + </rdf:Seq> + </sbox:guides> + </rdf:Description> + </rdf:RDF> +</x:xmpmeta> +<?xpacket end="w"?>`, testNamespace, entries)) +} + +// shorthandEntry puts a struct's fields in attributes. +func shorthandEntry(axis string, position float64, role, label string) string { + return fmt.Sprintf(` <rdf:li sbox:axis=%q sbox:position=%q sbox:role=%q sbox:label=%q/>`, + axis, fmt.Sprintf("%g", position), role, label) +} + +// expandedEntry puts them in child elements, which is what Adobe's toolkit +// rewrites shorthand into. +func expandedEntry(axis string, position float64, role, label string) string { + return fmt.Sprintf(` <rdf:li rdf:parseType="Resource"> + <sbox:axis>%s</sbox:axis> + <sbox:position>%g</sbox:position> + <sbox:role>%s</sbox:role> + <sbox:label>%s</sbox:label> + </rdf:li>`, axis, position, role, label) +} + +// annotatedFacePSD is the Face template with its eye line and chin named. +func annotatedFacePSD(entry func(string, float64, string, string) string) []byte { + packet := xmpPacket(strings.Join([]string{ + entry("Y", 0.025, "anchor", "Top of hair"), + entry("Y", 0.425, "anchor", "Eye line"), + entry("Y", 0.77, "reference", "Chin"), + entry("X", 0.5, "reference", "Centre"), + }, "\n")) + + var resources []byte + resources = append(resources, resourceBlock(guidesResourceID, guidesBlock(faceGuides))...) + resources = append(resources, resourceBlock(1060, packet)...) + return buildPSD(800, 1200, resources) +} + +func guideAt(t *testing.T, template Template, axis Axis, position float64) Guide { + t.Helper() + for _, g := range template.Guides { + if g.Axis == axis && g.Position > position-1e-6 && g.Position < position+1e-6 { + return g + } + } + t.Fatalf("no %s guide at %v in %+v", axis, position, template.Guides) + return Guide{} +} + +// Both encodings are legal XMP and we do not control which one a file comes +// back in, so both have to read the same. +func TestAnnotationsReadInEitherEncoding(t *testing.T) { + for _, tc := range []struct { + name string + entry func(string, float64, string, string) string + }{ + {"shorthand attributes", shorthandEntry}, + {"expanded elements", expandedEntry}, + } { + t.Run(tc.name, func(t *testing.T) { + template, err := Parse(annotatedFacePSD(tc.entry)) + if err != nil { + t.Fatalf("Parse: %v", err) + } + + eyeLine := guideAt(t, template, AxisY, 0.425) + if eyeLine.Label != "Eye line" { + t.Errorf("label %q, want %q", eyeLine.Label, "Eye line") + } + if eyeLine.Role != RoleAnchor { + t.Errorf("role %q, want %q", eyeLine.Role, RoleAnchor) + } + + chin := guideAt(t, template, AxisY, 0.77) + if chin.Role != RoleReference { + t.Errorf("chin role %q, want %q", chin.Role, RoleReference) + } + }) + } +} + +// An annotation reaching the wrong line would be worse than no annotation, so +// axis is part of the match and not only position. +func TestAnnotationsDoNotCrossAxes(t *testing.T) { + packet := xmpPacket(shorthandEntry("X", 0.5, "anchor", "A vertical line")) + + var resources []byte + resources = append(resources, resourceBlock(guidesResourceID, guidesBlock([]rawGuide{ + {px(400), rawVertical}, // X at 0.5 + {px(600), rawHorizontal}, // Y at 0.5 + }))...) + resources = append(resources, resourceBlock(1060, packet)...) + + template, err := Parse(buildPSD(800, 1200, resources)) + if err != nil { + t.Fatalf("Parse: %v", err) + } + + if got := guideAt(t, template, AxisX, 0.5); got.Label != "A vertical line" { + t.Errorf("vertical guide label %q, want %q", got.Label, "A vertical line") + } + if got := guideAt(t, template, AxisY, 0.5); got.Label != "" { + t.Errorf("horizontal guide label %q, want it unlabelled", got.Label) + } +} + +// Guides land on whole pixels, so an 800 px canvas puts its thirds at 33.25%. +// An author writing the round number should still match. +func TestAnnotationsToleratePixelRounding(t *testing.T) { + packet := xmpPacket(shorthandEntry("Y", 1.0/3.0, "reference", "Collarbone")) + + var resources []byte + resources = append(resources, resourceBlock(guidesResourceID, guidesBlock([]rawGuide{ + {px(399), rawHorizontal}, // 33.25% of 1200, as the real templates store it + }))...) + resources = append(resources, resourceBlock(1060, packet)...) + + template, err := Parse(buildPSD(800, 1200, resources)) + if err != nil { + t.Fatalf("Parse: %v", err) + } + if template.Guides[0].Label != "Collarbone" { + t.Errorf("label %q, want %q", template.Guides[0].Label, "Collarbone") + } +} + +// The tolerance must not be so wide that it reaches a neighbour. The closest +// pair in any real template is about twenty percentage points apart. +func TestAnnotationsDoNotReachDistantGuides(t *testing.T) { + packet := xmpPacket(shorthandEntry("Y", 0.5, "anchor", "Nowhere near")) + + var resources []byte + resources = append(resources, resourceBlock(guidesResourceID, guidesBlock([]rawGuide{ + {px(120), rawHorizontal}, // 10% + }))...) + resources = append(resources, resourceBlock(1060, packet)...) + + template, err := Parse(buildPSD(800, 1200, resources)) + if err != nil { + t.Fatalf("Parse: %v", err) + } + if template.Guides[0].Label != "" { + t.Errorf("label %q, want the guide left unlabelled", template.Guides[0].Label) + } +} + +// Geometry is the authority: XMP names lines, it does not add them. +func TestAnnotationsCannotInventGuides(t *testing.T) { + packet := xmpPacket(strings.Join([]string{ + shorthandEntry("Y", 0.425, "anchor", "Eye line"), + shorthandEntry("Y", 0.611, "anchor", "A line the template does not have"), + }, "\n")) + + var resources []byte + resources = append(resources, resourceBlock(guidesResourceID, guidesBlock(faceGuides))...) + resources = append(resources, resourceBlock(1060, packet)...) + + template, err := Parse(buildPSD(800, 1200, resources)) + if err != nil { + t.Fatalf("Parse: %v", err) + } + if len(template.Guides) != len(faceGuides) { + t.Fatalf("got %d guides, want %d", len(template.Guides), len(faceGuides)) + } + for _, g := range template.Guides { + if strings.Contains(g.Label, "does not have") { + t.Errorf("unmatched annotation became a guide: %+v", g) + } + } +} + +// Labels are a bonus and geometry is the contract, so nothing in the packet +// may cost an instance its template. +func TestUnreadableAnnotationsLeaveGeometryIntact(t *testing.T) { + for _, tc := range []struct { + name string + packet string + }{ + {"no XMP at all", ""}, + {"not XML", "\x00\x01 this is not markup"}, + {"truncated XML", `<x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF`}, + {"XMP from other software only", `<x:xmpmeta xmlns:x="adobe:ns:meta/"> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> + <rdf:Description xmlns:GIMP="http://www.gimp.org/xmp/"> + <GIMP:Version>2.10</GIMP:Version></rdf:Description></rdf:RDF></x:xmpmeta>`}, + {"our element, wrong namespace", `<x:xmpmeta xmlns:x="adobe:ns:meta/"> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> + <rdf:Description xmlns:sbox="http://example.invalid/other"> + <sbox:guides><rdf:Seq><rdf:li sbox:axis="Y" sbox:position="0.425" + sbox:label="Eye line"/></rdf:Seq></sbox:guides> + </rdf:Description></rdf:RDF></x:xmpmeta>`}, + } { + t.Run(tc.name, func(t *testing.T) { + var resources []byte + resources = append(resources, resourceBlock(guidesResourceID, guidesBlock(faceGuides))...) + if tc.packet != "" { + resources = append(resources, resourceBlock(1060, []byte(tc.packet))...) + } + + template, err := Parse(buildPSD(800, 1200, resources)) + if err != nil { + t.Fatalf("Parse: %v", err) + } + if len(template.Guides) != len(faceGuides) { + t.Errorf("got %d guides, want %d", len(template.Guides), len(faceGuides)) + } + for _, g := range template.Guides { + if g.Label != "" { + t.Errorf("expected no labels, got %q", g.Label) + } + } + }) + } +} + +// The element's own namespace has to be checked and not just its attributes': +// "guides" is an ordinary enough word that another vocabulary could use it, +// and its contents would then be read as ours. Only this shape catches that -- +// a packet where everything *inside* the element is in our namespace and only +// the element itself is not. +func TestGuidesElementMustBeInOurNamespace(t *testing.T) { + packet := []byte(`<x:xmpmeta xmlns:x="adobe:ns:meta/"> + <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> + <rdf:Description rdf:about="" + xmlns:other="http://example.invalid/some-other-vocabulary" + xmlns:sbox="` + testNamespace + `"> + <other:guides> + <rdf:Seq> + <rdf:li sbox:axis="Y" sbox:position="0.425" sbox:role="anchor" sbox:label="Eye line"/> + </rdf:Seq> + </other:guides> + </rdf:Description> + </rdf:RDF> +</x:xmpmeta>`) + + var resources []byte + resources = append(resources, resourceBlock(guidesResourceID, guidesBlock(faceGuides))...) + resources = append(resources, resourceBlock(1060, packet)...) + + template, err := Parse(buildPSD(800, 1200, resources)) + if err != nil { + t.Fatalf("Parse: %v", err) + } + if got := guideAt(t, template, AxisY, 0.425); got.Label != "" { + t.Errorf("read %q out of another vocabulary's guides element", got.Label) + } +} + +// A typo in a role should cost the distinction it got wrong, not the name of +// the line. +func TestUnknownRoleKeepsTheLabel(t *testing.T) { + packet := xmpPacket(shorthandEntry("Y", 0.425, "anchour", "Eye line")) + + var resources []byte + resources = append(resources, resourceBlock(guidesResourceID, guidesBlock(faceGuides))...) + resources = append(resources, resourceBlock(1060, packet)...) + + template, err := Parse(buildPSD(800, 1200, resources)) + if err != nil { + t.Fatalf("Parse: %v", err) + } + + eyeLine := guideAt(t, template, AxisY, 0.425) + if eyeLine.Label != "Eye line" { + t.Errorf("label %q, want it kept", eyeLine.Label) + } + if eyeLine.Role != "" { + t.Errorf("role %q, want it empty", eyeLine.Role) + } +} + +func TestAnnotationsAcceptLooseCasing(t *testing.T) { + packet := xmpPacket(shorthandEntry("y", 0.425, "Anchor", "Eye line")) + + var resources []byte + resources = append(resources, resourceBlock(guidesResourceID, guidesBlock(faceGuides))...) + resources = append(resources, resourceBlock(1060, packet)...) + + template, err := Parse(buildPSD(800, 1200, resources)) + if err != nil { + t.Fatalf("Parse: %v", err) + } + if got := guideAt(t, template, AxisY, 0.425); got.Role != RoleAnchor { + t.Errorf("role %q, want %q", got.Role, RoleAnchor) + } +} + +// An entry naming nothing must not consume the guide it happens to sit on. A +// blank one first would otherwise take the match and leave the entry that +// actually names the line unread -- and the guide would come back with the +// role and label it would have had if there were no annotation at all, which +// is indistinguishable from an unannotated template. +func TestBlankAnnotationsDoNotConsumeAGuide(t *testing.T) { + packet := xmpPacket(strings.Join([]string{ + shorthandEntry("Y", 0.425, "", ""), + shorthandEntry("Y", 0.425, "anchor", "Eye line"), + }, "\n")) + + var resources []byte + resources = append(resources, resourceBlock(guidesResourceID, guidesBlock(faceGuides))...) + resources = append(resources, resourceBlock(1060, packet)...) + + template, err := Parse(buildPSD(800, 1200, resources)) + if err != nil { + t.Fatalf("Parse: %v", err) + } + + eyeLine := guideAt(t, template, AxisY, 0.425) + if eyeLine.Label != "Eye line" { + t.Errorf("label %q, want %q", eyeLine.Label, "Eye line") + } + if eyeLine.Role != RoleAnchor { + t.Errorf("role %q, want %q", eyeLine.Role, RoleAnchor) + } +} + +// pivotEntry writes an entry carrying the pivot flag, in either encoding. The +// flag is a separate helper rather than another parameter on the two above, +// which every existing test would otherwise have to pass an empty string to. +func shorthandPivot(axis string, position float64, role, label, pivot string) string { + return fmt.Sprintf(` <rdf:li sbox:axis=%q sbox:position=%q sbox:role=%q sbox:label=%q sbox:pivot=%q/>`, + axis, fmt.Sprintf("%g", position), role, label, pivot) +} + +func expandedPivot(axis string, position float64, role, label, pivot string) string { + return fmt.Sprintf(` <rdf:li rdf:parseType="Resource"> + <sbox:axis>%s</sbox:axis> + <sbox:position>%g</sbox:position> + <sbox:role>%s</sbox:role> + <sbox:label>%s</sbox:label> + <sbox:pivot>%s</sbox:pivot> + </rdf:li>`, axis, position, role, label, pivot) +} + +// parseWithEntries builds a Face template whose XMP carries exactly these +// entries. +func parseWithEntries(t *testing.T, entries ...string) Template { + t.Helper() + + var resources []byte + resources = append(resources, resourceBlock(guidesResourceID, guidesBlock(faceGuides))...) + resources = append(resources, resourceBlock(1060, xmpPacket(strings.Join(entries, "\n")))...) + + template, err := Parse(buildPSD(800, 1200, resources)) + if err != nil { + t.Fatalf("Parse: %v", err) + } + return template +} + +// The pivot rides in the packet like the role does, so it has to survive the +// same round trip through Adobe's toolkit -- which rewrites shorthand into the +// expanded form. +func TestPivotIsReadInEitherEncoding(t *testing.T) { + for _, tc := range []struct { + name string + entry func(string, float64, string, string, string) string + }{ + {"shorthand", shorthandPivot}, + {"expanded", expandedPivot}, + } { + t.Run(tc.name, func(t *testing.T) { + template := parseWithEntries(t, + tc.entry("Y", 0.425, "reference", "Eye line", "true"), + tc.entry("Y", 0.77, "anchor", "Chin", ""), + ) + + if eyeLine := guideAt(t, template, AxisY, 0.425); !eyeLine.Pivot { + t.Error("the eye line did not come back as the pivot") + } + if chin := guideAt(t, template, AxisY, 0.77); chin.Pivot { + t.Error("the chin came back as a pivot without claiming to be one") + } + }) + } +} + +// Role and pivot are independent, which is the whole reason they are separate +// fields: the softest line in a headshot is the right one to resize about. +func TestPivotIsIndependentOfRole(t *testing.T) { + template := parseWithEntries(t, + shorthandPivot("Y", 0.425, "reference", "Eye line", "true"), + shorthandPivot("Y", 0.025, "anchor", "Top of hair", ""), + ) + + eyeLine := guideAt(t, template, AxisY, 0.425) + if eyeLine.Role != RoleReference || !eyeLine.Pivot { + t.Errorf("eye line is role %q pivot %v, want a REFERENCE that is also the pivot", + eyeLine.Role, eyeLine.Pivot) + } + + hair := guideAt(t, template, AxisY, 0.025) + if hair.Role != RoleAnchor || hair.Pivot { + t.Errorf("top of hair is role %q pivot %v, want an ANCHOR that is not the pivot", + hair.Role, hair.Pivot) + } +} + +// A guide that says nothing about being the pivot is not the pivot. Worth +// stating, because the whole corpus predates the flag and every line in it +// arrives this way. +func TestAGuideWithNoPivotFlagIsNotThePivot(t *testing.T) { + template := parseWithEntries(t, shorthandEntry("Y", 0.425, "anchor", "Eye line")) + + if guideAt(t, template, AxisY, 0.425).Pivot { + t.Error("a guide with no pivot flag came back as the pivot") + } +} + +func TestPivotAcceptsTheFormsAPacketCarries(t *testing.T) { + for _, tc := range []struct { + written string + want bool + }{ + // What Adobe's toolkit and a hand-written packet respectively produce. + {"true", true}, + {"True", true}, + {"1", true}, + {"yes", true}, + {"false", false}, + {"0", false}, + {"", false}, + // A typo costs the flag rather than being guessed at. + {"ture", false}, + } { + t.Run(tc.written, func(t *testing.T) { + template := parseWithEntries(t, + shorthandPivot("Y", 0.425, "reference", "Eye line", tc.written)) + + if got := guideAt(t, template, AxisY, 0.425).Pivot; got != tc.want { + t.Errorf("pivot=%q read as %v, want %v", tc.written, got, tc.want) + } + }) + } +} + +// An entry naming only the pivot still has something to say. The empty-entry +// rule exists so a typo'd role with a blank label cannot consume a guide and +// leave a better entry unread -- it must not also throw away a flag. +func TestAPivotAloneIsEnoughToAnnotate(t *testing.T) { + template := parseWithEntries(t, shorthandPivot("Y", 0.425, "", "", "true")) + + eyeLine := guideAt(t, template, AxisY, 0.425) + if !eyeLine.Pivot { + t.Error("an entry carrying nothing but a pivot was discarded as empty") + } + if eyeLine.Role != "" || eyeLine.Label != "" { + t.Errorf("it also invented role %q label %q", eyeLine.Role, eyeLine.Label) + } +} + +// A frame cannot be held still at two points on one axis and have anything +// left for the drag to change. Neither wins: the order guides arrive in is the +// order block 1032 stores them, so picking by it would make the behaviour +// depend on something no author can see. Both cleared leaves the axis +// resizing about its centre, which is what an unmarked template does anyway. +func TestTwoPivotsOnAnAxisLeaveNeither(t *testing.T) { + template := parseWithEntries(t, + shorthandPivot("Y", 0.425, "reference", "Eye line", "true"), + shorthandPivot("Y", 0.77, "anchor", "Chin", "true"), + ) + + for _, position := range []float64{0.425, 0.77} { + if guide := guideAt(t, template, AxisY, position); guide.Pivot { + t.Errorf("guide at %v kept its pivot despite the axis claiming two", position) + } + } +} + +// The two axes are independent. A template that lines a subject up vertically +// and leaves the horizontal to the thirds is the ordinary case. +func TestAPivotOnEachAxisIsKept(t *testing.T) { + template := parseWithEntries(t, + shorthandPivot("Y", 0.425, "reference", "Eye line", "true"), + shorthandPivot("X", 0.5, "reference", "Centre", "true"), + ) + + if !guideAt(t, template, AxisY, 0.425).Pivot { + t.Error("the Y pivot was dropped") + } + if !guideAt(t, template, AxisX, 0.5).Pivot { + t.Error("the X pivot was dropped") + } +} + +// An axis that over-claims must not cost the other one its pivot. +func TestAnAmbiguousAxisDoesNotDisarmTheOther(t *testing.T) { + template := parseWithEntries(t, + shorthandPivot("Y", 0.425, "reference", "Eye line", "true"), + shorthandPivot("Y", 0.77, "anchor", "Chin", "true"), + shorthandPivot("X", 0.5, "reference", "Centre", "true"), + ) + + if !guideAt(t, template, AxisX, 0.5).Pivot { + t.Error("the X pivot was dropped because Y claimed two") + } +} diff --git a/internal/image/sort.go b/internal/image/sort.go index 4055355bd..ebf6d21e3 100644 --- a/internal/image/sort.go +++ b/internal/image/sort.go @@ -4,9 +4,71 @@ import ( "math" "sort" + "github.com/gofrs/uuid" + "github.com/stashapp/stash-box/internal/models" ) +// Unranked is the tuple component for an image carrying no type from a group. +// Such images sort last within that dimension. +const Unranked = math.MaxInt + +// RankTuple orders an image against the instance's vocabulary, one component +// per group in group priority order. Tuples compare lexicographically. +type RankTuple []int + +// at reads a component, treating a missing one as Unranked, so an image with no +// assignments can be absent from the ranks map entirely. +func (t RankTuple) at(i int) int { + if i < len(t) { + return t[i] + } + return Unranked +} + +func (t RankTuple) before(other RankTuple) bool { + for i := range max(len(t), len(other)) { + if a, b := t.at(i), other.at(i); a != b { + return a < b + } + } + return false +} + +// OrderByType sorts images by their rank tuple, breaking ties with the +// entity's existing comparator +// +// The sort must stay stable: equally-ranked images would otherwise come back in +// an arbitrary order, which surfaces as the primary image changing when nobody +// edited anything +func OrderByType(images []models.Image, ranks map[uuid.UUID]RankTuple, tiebreak func([]models.Image)) { + tiebreak(images) + + sort.SliceStable(images, func(a, b int) bool { + return ranks[images[a].ID].before(ranks[images[b].ID]) + }) +} + +// NewestFirst wraps a tiebreak so that images equally ranked come back most +// recent first, undated last +// +// Dates are partial ISO 8601 and compare as strings: "2019" < "2019-06" < +// "2019-06-15", so a bare year sorts as the start of its year. Nothing needs +// parsing and there is no timezone to be wrong about +func NewestFirst(dates map[uuid.UUID]*string, tiebreak func([]models.Image)) func([]models.Image) { + return func(images []models.Image) { + tiebreak(images) + + sort.SliceStable(images, func(a, b int) bool { + left, right := dates[images[a].ID], dates[images[b].ID] + if left == nil || right == nil { + return left != nil && right == nil + } + return *left > *right + }) + } +} + // Sorts by "most" to "least" landscape, i.e. largest to smallest aspect ratio; ties broken by largest --> smallest width. func OrderLandscape(p []models.Image) { sort.Slice(p, func(a, b int) bool { diff --git a/internal/image/sort_test.go b/internal/image/sort_test.go index 91f3de05c..5139b34f1 100644 --- a/internal/image/sort_test.go +++ b/internal/image/sort_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" + "github.com/gofrs/uuid" "github.com/stashapp/stash-box/internal/models" "github.com/stretchr/testify/assert" ) @@ -172,3 +173,207 @@ func TestOrderPortrait(t *testing.T) { }) } } + +// The tiebreak's order has to survive among equally ranked images, or the +// primary image changes without anyone editing anything +// +// Go's sort only switches from insertion sort to pdqsort above a small +// threshold, so the slice has to be long! The ranks also have +// have to interleave: given a comparator that reports everything equal, +// pdqsort recognises an already-sorted run and leaves it alone, so a +// uniformly-ranked gallery passes even with sort.Slice +func TestOrderByTypeIsStableAcrossEqualRanks(t *testing.T) { + const count = 60 + + images := make([]models.Image, count) + ranks := make(map[uuid.UUID]RankTuple, count) + for i := range images { + id, err := uuid.NewV7() + assert.NoError(t, err) + + // Identical dimensions, so the tiebreak cannot reorder them either + images[i] = models.Image{ID: id, Width: 400, Height: 600} + ranks[id] = RankTuple{i % 2} + } + + // Sorting must move every odd-positioned image after every even one: + // the relative order within each half is stability's job + var expected []uuid.UUID + for _, remainder := range []int{0, 1} { + for i, img := range images { + if i%2 == remainder { + expected = append(expected, img.ID) + } + } + } + + OrderByType(images, ranks, func([]models.Image) {}) + + actual := make([]uuid.UUID, count) + for i, img := range images { + actual[i] = img.ID + } + assert.Equal(t, expected, actual, "equally ranked images must keep the tiebreak's order") +} + +func TestOrderByTypeRanksUntypedLast(t *testing.T) { + id := func() uuid.UUID { + v, err := uuid.NewV7() + assert.NoError(t, err) + return v + } + + typed, untyped, partly := id(), id(), id() + images := []models.Image{ + {ID: untyped, Width: 400, Height: 600}, + {ID: partly, Width: 400, Height: 600}, + {ID: typed, Width: 400, Height: 600}, + } + + ranks := map[uuid.UUID]RankTuple{ + typed: {0, 0}, + // Ranked in the first dimension only; the missing second component + // must read as Unranked rather than as zero + partly: {0}, + } + + OrderByType(images, ranks, func([]models.Image) {}) + + assert.Equal(t, []uuid.UUID{typed, partly, untyped}, + []uuid.UUID{images[0].ID, images[1].ID, images[2].ID}) +} + +// Ties on rank fall to the date, most recent first. Without this two +// equally-labelled images have no order anyone chose: they come back in +// whatever the aspect sort made of them, which is not what a gallery means +func TestNewestFirstOrdersDatedImagesMostRecentFirst(t *testing.T) { + id := func() uuid.UUID { + v, err := uuid.NewV7() + assert.NoError(t, err) + return v + } + at := func(s string) *string { return &s } + + oldest, middle, newest := id(), id(), id() + images := []models.Image{ + {ID: middle, Width: 400, Height: 600}, + {ID: oldest, Width: 400, Height: 600}, + {ID: newest, Width: 400, Height: 600}, + } + dates := map[uuid.UUID]*string{ + oldest: at("2019-06"), + middle: at("2021"), + newest: at("2023-01-15"), + } + + NewestFirst(dates, func([]models.Image) {})(images) + + assert.Equal(t, []uuid.UUID{newest, middle, oldest}, + []uuid.UUID{images[0].ID, images[1].ID, images[2].ID}) +} + +func TestNewestFirstReadsABareYearAsTheStartOfIt(t *testing.T) { + id := func() uuid.UUID { + v, err := uuid.NewV7() + assert.NoError(t, err) + return v + } + at := func(s string) *string { return &s } + + year, june := id(), id() + images := []models.Image{ + {ID: year, Width: 400, Height: 600}, + {ID: june, Width: 400, Height: 600}, + } + dates := map[uuid.UUID]*string{year: at("2019"), june: at("2019-06")} + + NewestFirst(dates, func([]models.Image) {})(images) + + assert.Equal(t, []uuid.UUID{june, year}, + []uuid.UUID{images[0].ID, images[1].ID}) +} + +// A date is a claim someone made; its absence is not a claim that the image is +// old. So undated images go last rather than being treated as ancient +func TestNewestFirstPutsUndatedImagesLast(t *testing.T) { + id := func() uuid.UUID { + v, err := uuid.NewV7() + assert.NoError(t, err) + return v + } + at := func(s string) *string { return &s } + + dated, undated, alsoOld := id(), id(), id() + images := []models.Image{ + {ID: undated, Width: 400, Height: 600}, + {ID: alsoOld, Width: 400, Height: 600}, + {ID: dated, Width: 400, Height: 600}, + } + dates := map[uuid.UUID]*string{dated: at("2023"), alsoOld: at("1999")} + + NewestFirst(dates, func([]models.Image) {})(images) + + assert.Equal(t, []uuid.UUID{dated, alsoOld, undated}, + []uuid.UUID{images[0].ID, images[1].ID, images[2].ID}) +} + +// Images that tie on date keep whatever the weaker tiebreak decided, or a +// gallery reorders itself between requests +// +// Half dated with the same date and half undated, interleaved, so the sort has +// real work to do: an unstable one has to move every dated image up past an +// undated one, and that is when it scrambles the ties it is carrying +func TestNewestFirstLeavesTiedImagesToTheTiebreak(t *testing.T) { + const count = 40 + sameDate := "2020" + + images := make([]models.Image, count) + dates := map[uuid.UUID]*string{} + var wantDated, wantUndated []uuid.UUID + for i := range images { + v, err := uuid.NewV7() + assert.NoError(t, err) + images[i] = models.Image{ID: v, Width: 400, Height: 600} + + if i%2 == 0 { + dates[v] = &sameDate + wantDated = append(wantDated, v) + } else { + wantUndated = append(wantUndated, v) + } + } + + NewestFirst(dates, func([]models.Image) {})(images) + + after := make([]uuid.UUID, count) + for i, img := range images { + after[i] = img.ID + } + assert.Equal(t, append(wantDated, wantUndated...), after, + "dated first, and each group in the order it arrived") +} + +// The composition the resolvers use: rank first, then date, then shape. A +// newer image must not climb above a better-ranked one. +func TestRankOutranksDate(t *testing.T) { + id := func() uuid.UUID { + v, err := uuid.NewV7() + assert.NoError(t, err) + return v + } + at := func(s string) *string { return &s } + + ranked, newer := id(), id() + images := []models.Image{ + {ID: newer, Width: 400, Height: 600}, + {ID: ranked, Width: 400, Height: 600}, + } + ranks := map[uuid.UUID]RankTuple{ranked: {0}} + dates := map[uuid.UUID]*string{ranked: at("1999"), newer: at("2024")} + + OrderByType(images, ranks, NewestFirst(dates, func([]models.Image) {})) + + assert.Equal(t, []uuid.UUID{ranked, newer}, + []uuid.UUID{images[0].ID, images[1].ID}, + "a label the admin ranked beats a date") +} diff --git a/internal/models/generated_exec.go b/internal/models/generated_exec.go index be98a70c5..620e59531 100644 --- a/internal/models/generated_exec.go +++ b/internal/models/generated_exec.go @@ -35,6 +35,7 @@ type ResolverRoot interface { EditComment() EditCommentResolver EditVote() EditVoteResolver Image() ImageResolver + ImageType() ImageTypeResolver ModAudit() ModAuditResolver Mutation() MutationResolver Notification() NotificationResolver @@ -104,6 +105,41 @@ type ComplexityRoot struct { Comment func(childComplexity int) int } + CropGuide struct { + Axis func(childComplexity int) int + Label func(childComplexity int) int + Pivot func(childComplexity int) int + Position func(childComplexity int) int + Role func(childComplexity int) int + } + + CropKnot struct { + Anchor func(childComplexity int) int + ControlIn func(childComplexity int) int + ControlOut func(childComplexity int) int + } + + CropPoint struct { + X func(childComplexity int) int + Y func(childComplexity int) int + } + + CropShape struct { + Label func(childComplexity int) int + Subpaths func(childComplexity int) int + } + + CropSubpath struct { + Closed func(childComplexity int) int + Knots func(childComplexity int) int + } + + CropTemplate struct { + AspectRatio func(childComplexity int) int + Guides func(childComplexity int) int + Shapes func(childComplexity int) int + } + DownvoteOwnEdit struct { Edit func(childComplexity int) int } @@ -251,6 +287,35 @@ type ComplexityRoot struct { Width func(childComplexity int) int } + ImageAssignmentChange struct { + AddedTypes func(childComplexity int) int + Date func(childComplexity int) int + DateChanged func(childComplexity int) int + Image func(childComplexity int) int + RemovedTypes func(childComplexity int) int + } + + ImageType struct { + ConflictsWith func(childComplexity int) int + CropTemplate func(childComplexity int) int + Description func(childComplexity int) int + Enabled func(childComplexity int) int + Key func(childComplexity int) int + Name func(childComplexity int) int + SortOrder func(childComplexity int) int + ValidTypes func(childComplexity int) int + } + + ImageTypeGroup struct { + Description func(childComplexity int) int + Enabled func(childComplexity int) int + Exclusive func(childComplexity int) int + Key func(childComplexity int) int + Name func(childComplexity int) int + SortOrder func(childComplexity int) int + Types func(childComplexity int) int + } + InviteKey struct { Expires func(childComplexity int) int ID func(childComplexity int) int @@ -294,6 +359,8 @@ type ComplexityRoot struct { HideEditComment func(childComplexity int, input HideEditCommentInput) int ImageCreate func(childComplexity int, input ImageCreateInput) int ImageDestroy func(childComplexity int, input ImageDestroyInput) int + ImageTypeOrderUpdate func(childComplexity int, input ImageTypeOrderInput) int + ImageTypeSetEnabled func(childComplexity int, input ImageTypeEnabledInput) int MarkNotificationsRead func(childComplexity int, notification *MarkNotificationReadInput) int NewUser func(childComplexity int, input NewUserInput) int PerformerCreate func(childComplexity int, input PerformerCreateInput) int @@ -337,6 +404,7 @@ type ComplexityRoot struct { TagEditUpdate func(childComplexity int, id uuid.UUID, input TagEditInput) int TagUpdate func(childComplexity int, input TagUpdateInput) int UpdateEditComment func(childComplexity int, input UpdateEditCommentInput) int + UpdateImageTypePreferences func(childComplexity int, input ImageTypePreferencesInput) int UpdateNotificationSubscriptions func(childComplexity int, subscriptions []NotificationEnum) int UserCreate func(childComplexity int, input UserCreateInput) int UserDestroy func(childComplexity int, input UserDestroyInput) int @@ -386,6 +454,8 @@ type ComplexityRoot struct { Scenes func(childComplexity int, input *PerformerScenesInput) int Studios func(childComplexity int, studioID *uuid.UUID) int Tattoos func(childComplexity int) int + Thumbnail func(childComplexity int) int + TypedImages func(childComplexity int) int Updated func(childComplexity int) int Urls func(childComplexity int) int WaistSize func(childComplexity int) int @@ -442,6 +512,7 @@ type ComplexityRoot struct { HairColor func(childComplexity int) int Height func(childComplexity int) int HipSize func(childComplexity int) int + ImageChanges func(childComplexity int) int Images func(childComplexity int) int Name func(childComplexity int) int Piercings func(childComplexity int) int @@ -451,6 +522,7 @@ type ComplexityRoot struct { RemovedTattoos func(childComplexity int) int RemovedUrls func(childComplexity int) int Tattoos func(childComplexity int) int + TypedImages func(childComplexity int) int Urls func(childComplexity int) int WaistSize func(childComplexity int) int } @@ -491,6 +563,7 @@ type ComplexityRoot struct { FingerprintClusters func(childComplexity int, input FingerprintClustersInput) int GetConfig func(childComplexity int) int GetUnreadNotificationCount func(childComplexity int) int + ImageTypeGroups func(childComplexity int, target *ImageTypeScopeEnum, includeDisabled *bool) int Me func(childComplexity int) int QueryEdits func(childComplexity int, input EditQueryInput) int QueryExistingPerformer func(childComplexity int, input QueryExistingPerformerInput) int @@ -743,6 +816,12 @@ type ComplexityRoot struct { RemovedAliases func(childComplexity int) int } + TypedImage struct { + Date func(childComplexity int) int + Image func(childComplexity int) int + Types func(childComplexity int) int + } + URL struct { Site func(childComplexity int) int Type func(childComplexity int) int @@ -765,6 +844,8 @@ type ComplexityRoot struct { EditCount func(childComplexity int) int Email func(childComplexity int) int ID func(childComplexity int) int + ImageTypeGroupPreferences func(childComplexity int) int + ImageTypePreferences func(childComplexity int) int InviteCodes func(childComplexity int) int InviteTokens func(childComplexity int) int InvitedBy func(childComplexity int) int @@ -854,6 +935,9 @@ type EditVoteResolver interface { type ImageResolver interface { URL(ctx context.Context, obj *Image) (string, error) } +type ImageTypeResolver interface { + CropTemplate(ctx context.Context, obj *ImageType) (*CropTemplate, error) +} type ModAuditResolver interface { Action(ctx context.Context, obj *ModAudit) (ModAuditActionEnum, error) User(ctx context.Context, obj *ModAudit) (*User, error) @@ -892,6 +976,8 @@ type MutationResolver interface { SiteCategoryCreate(ctx context.Context, input SiteCategoryCreateInput) (*SiteCategory, error) SiteCategoryUpdate(ctx context.Context, input SiteCategoryUpdateInput) (*SiteCategory, error) SiteCategoryDestroy(ctx context.Context, input SiteCategoryDestroyInput) (bool, error) + ImageTypeOrderUpdate(ctx context.Context, input ImageTypeOrderInput) ([]ImageTypeGroup, error) + ImageTypeSetEnabled(ctx context.Context, input ImageTypeEnabledInput) ([]ImageTypeGroup, error) RegenerateAPIKey(ctx context.Context, userID *uuid.UUID) (string, error) ResetPassword(ctx context.Context, input ResetPasswordInput) (bool, error) ChangePassword(ctx context.Context, input UserChangePasswordInput) (bool, error) @@ -925,6 +1011,7 @@ type MutationResolver interface { FavoriteStudio(ctx context.Context, id uuid.UUID, favorite bool) (bool, error) MarkNotificationsRead(ctx context.Context, notification *MarkNotificationReadInput) (bool, error) UpdateNotificationSubscriptions(ctx context.Context, subscriptions []NotificationEnum) (bool, error) + UpdateImageTypePreferences(ctx context.Context, input ImageTypePreferencesInput) (bool, error) } type NotificationResolver interface { Created(ctx context.Context, obj *Notification) (*time.Time, error) @@ -945,6 +1032,8 @@ type PerformerResolver interface { Tattoos(ctx context.Context, obj *Performer) ([]BodyModification, error) Piercings(ctx context.Context, obj *Performer) ([]BodyModification, error) Images(ctx context.Context, obj *Performer) ([]Image, error) + TypedImages(ctx context.Context, obj *Performer) ([]TypedImage, error) + Thumbnail(ctx context.Context, obj *Performer) (*Image, error) Edits(ctx context.Context, obj *Performer) ([]Edit, error) SceneCount(ctx context.Context, obj *Performer) (int, error) @@ -970,6 +1059,8 @@ type PerformerEditResolver interface { AddedImages(ctx context.Context, obj *PerformerEdit) ([]Image, error) RemovedImages(ctx context.Context, obj *PerformerEdit) ([]Image, error) + ImageChanges(ctx context.Context, obj *PerformerEdit) ([]ImageAssignmentChange, error) + TypedImages(ctx context.Context, obj *PerformerEdit) ([]TypedImage, error) Aliases(ctx context.Context, obj *PerformerEdit) ([]string, error) Urls(ctx context.Context, obj *PerformerEdit) ([]URL, error) @@ -999,6 +1090,7 @@ type QueryResolver interface { FindSiteCategory(ctx context.Context, id int) (*SiteCategory, error) QuerySiteCategories(ctx context.Context) (*QuerySiteCategoriesResultType, error) FetchSiteFavicons(ctx context.Context, url string) ([]SiteFavicon, error) + ImageTypeGroups(ctx context.Context, target *ImageTypeScopeEnum, includeDisabled *bool) ([]ImageTypeGroup, error) FindEdit(ctx context.Context, id uuid.UUID) (*Edit, error) QueryEdits(ctx context.Context, input EditQueryInput) (*EditQuery, error) FindUser(ctx context.Context, id *uuid.UUID, username *string) (*User, error) @@ -1137,6 +1229,8 @@ type UserResolver interface { Roles(ctx context.Context, obj *User) ([]RoleEnum, error) NotificationSubscriptions(ctx context.Context, obj *User) ([]NotificationEnum, error) + ImageTypePreferences(ctx context.Context, obj *User) ([]ImageTypeEnum, error) + ImageTypeGroupPreferences(ctx context.Context, obj *User) ([]ImageTypeGroupEnum, error) VoteCount(ctx context.Context, obj *User) (*UserVoteCount, error) EditCount(ctx context.Context, obj *User) (*UserEditCount, error) @@ -1257,6 +1351,114 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.CommentVotedEdit.Comment(childComplexity), true + case "CropGuide.axis": + if e.ComplexityRoot.CropGuide.Axis == nil { + break + } + + return e.ComplexityRoot.CropGuide.Axis(childComplexity), true + case "CropGuide.label": + if e.ComplexityRoot.CropGuide.Label == nil { + break + } + + return e.ComplexityRoot.CropGuide.Label(childComplexity), true + case "CropGuide.pivot": + if e.ComplexityRoot.CropGuide.Pivot == nil { + break + } + + return e.ComplexityRoot.CropGuide.Pivot(childComplexity), true + case "CropGuide.position": + if e.ComplexityRoot.CropGuide.Position == nil { + break + } + + return e.ComplexityRoot.CropGuide.Position(childComplexity), true + case "CropGuide.role": + if e.ComplexityRoot.CropGuide.Role == nil { + break + } + + return e.ComplexityRoot.CropGuide.Role(childComplexity), true + + case "CropKnot.anchor": + if e.ComplexityRoot.CropKnot.Anchor == nil { + break + } + + return e.ComplexityRoot.CropKnot.Anchor(childComplexity), true + case "CropKnot.control_in": + if e.ComplexityRoot.CropKnot.ControlIn == nil { + break + } + + return e.ComplexityRoot.CropKnot.ControlIn(childComplexity), true + case "CropKnot.control_out": + if e.ComplexityRoot.CropKnot.ControlOut == nil { + break + } + + return e.ComplexityRoot.CropKnot.ControlOut(childComplexity), true + + case "CropPoint.x": + if e.ComplexityRoot.CropPoint.X == nil { + break + } + + return e.ComplexityRoot.CropPoint.X(childComplexity), true + case "CropPoint.y": + if e.ComplexityRoot.CropPoint.Y == nil { + break + } + + return e.ComplexityRoot.CropPoint.Y(childComplexity), true + + case "CropShape.label": + if e.ComplexityRoot.CropShape.Label == nil { + break + } + + return e.ComplexityRoot.CropShape.Label(childComplexity), true + case "CropShape.subpaths": + if e.ComplexityRoot.CropShape.Subpaths == nil { + break + } + + return e.ComplexityRoot.CropShape.Subpaths(childComplexity), true + + case "CropSubpath.closed": + if e.ComplexityRoot.CropSubpath.Closed == nil { + break + } + + return e.ComplexityRoot.CropSubpath.Closed(childComplexity), true + case "CropSubpath.knots": + if e.ComplexityRoot.CropSubpath.Knots == nil { + break + } + + return e.ComplexityRoot.CropSubpath.Knots(childComplexity), true + + case "CropTemplate.aspect_ratio": + if e.ComplexityRoot.CropTemplate.AspectRatio == nil { + break + } + + return e.ComplexityRoot.CropTemplate.AspectRatio(childComplexity), true + case "CropTemplate.guides": + if e.ComplexityRoot.CropTemplate.Guides == nil { + break + } + + return e.ComplexityRoot.CropTemplate.Guides(childComplexity), true + case "CropTemplate.shapes": + if e.ComplexityRoot.CropTemplate.Shapes == nil { + break + } + + return e.ComplexityRoot.CropTemplate.Shapes(childComplexity), true + case "DownvoteOwnEdit.edit": if e.ComplexityRoot.DownvoteOwnEdit.Edit == nil { break @@ -1748,6 +1950,129 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.Image.Width(childComplexity), true + case "ImageAssignmentChange.added_types": + if e.ComplexityRoot.ImageAssignmentChange.AddedTypes == nil { + break + } + + return e.ComplexityRoot.ImageAssignmentChange.AddedTypes(childComplexity), true + case "ImageAssignmentChange.date": + if e.ComplexityRoot.ImageAssignmentChange.Date == nil { + break + } + + return e.ComplexityRoot.ImageAssignmentChange.Date(childComplexity), true + case "ImageAssignmentChange.date_changed": + if e.ComplexityRoot.ImageAssignmentChange.DateChanged == nil { + break + } + + return e.ComplexityRoot.ImageAssignmentChange.DateChanged(childComplexity), true + case "ImageAssignmentChange.image": + if e.ComplexityRoot.ImageAssignmentChange.Image == nil { + break + } + + return e.ComplexityRoot.ImageAssignmentChange.Image(childComplexity), true + case "ImageAssignmentChange.removed_types": + if e.ComplexityRoot.ImageAssignmentChange.RemovedTypes == nil { + break + } + + return e.ComplexityRoot.ImageAssignmentChange.RemovedTypes(childComplexity), true + + case "ImageType.conflicts_with": + if e.ComplexityRoot.ImageType.ConflictsWith == nil { + break + } + + return e.ComplexityRoot.ImageType.ConflictsWith(childComplexity), true + case "ImageType.crop_template": + if e.ComplexityRoot.ImageType.CropTemplate == nil { + break + } + + return e.ComplexityRoot.ImageType.CropTemplate(childComplexity), true + case "ImageType.description": + if e.ComplexityRoot.ImageType.Description == nil { + break + } + + return e.ComplexityRoot.ImageType.Description(childComplexity), true + case "ImageType.enabled": + if e.ComplexityRoot.ImageType.Enabled == nil { + break + } + + return e.ComplexityRoot.ImageType.Enabled(childComplexity), true + case "ImageType.key": + if e.ComplexityRoot.ImageType.Key == nil { + break + } + + return e.ComplexityRoot.ImageType.Key(childComplexity), true + case "ImageType.name": + if e.ComplexityRoot.ImageType.Name == nil { + break + } + + return e.ComplexityRoot.ImageType.Name(childComplexity), true + case "ImageType.sort_order": + if e.ComplexityRoot.ImageType.SortOrder == nil { + break + } + + return e.ComplexityRoot.ImageType.SortOrder(childComplexity), true + case "ImageType.valid_types": + if e.ComplexityRoot.ImageType.ValidTypes == nil { + break + } + + return e.ComplexityRoot.ImageType.ValidTypes(childComplexity), true + + case "ImageTypeGroup.description": + if e.ComplexityRoot.ImageTypeGroup.Description == nil { + break + } + + return e.ComplexityRoot.ImageTypeGroup.Description(childComplexity), true + case "ImageTypeGroup.enabled": + if e.ComplexityRoot.ImageTypeGroup.Enabled == nil { + break + } + + return e.ComplexityRoot.ImageTypeGroup.Enabled(childComplexity), true + case "ImageTypeGroup.exclusive": + if e.ComplexityRoot.ImageTypeGroup.Exclusive == nil { + break + } + + return e.ComplexityRoot.ImageTypeGroup.Exclusive(childComplexity), true + case "ImageTypeGroup.key": + if e.ComplexityRoot.ImageTypeGroup.Key == nil { + break + } + + return e.ComplexityRoot.ImageTypeGroup.Key(childComplexity), true + case "ImageTypeGroup.name": + if e.ComplexityRoot.ImageTypeGroup.Name == nil { + break + } + + return e.ComplexityRoot.ImageTypeGroup.Name(childComplexity), true + case "ImageTypeGroup.sort_order": + if e.ComplexityRoot.ImageTypeGroup.SortOrder == nil { + break + } + + return e.ComplexityRoot.ImageTypeGroup.SortOrder(childComplexity), true + case "ImageTypeGroup.types": + if e.ComplexityRoot.ImageTypeGroup.Types == nil { + break + } + + return e.ComplexityRoot.ImageTypeGroup.Types(childComplexity), true + case "InviteKey.expires": if e.ComplexityRoot.InviteKey.Expires == nil { break @@ -2034,6 +2359,28 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin } return e.ComplexityRoot.Mutation.ImageDestroy(childComplexity, args["input"].(ImageDestroyInput)), true + case "Mutation.imageTypeOrderUpdate": + if e.ComplexityRoot.Mutation.ImageTypeOrderUpdate == nil { + break + } + + args, err := ec.field_Mutation_imageTypeOrderUpdate_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.ComplexityRoot.Mutation.ImageTypeOrderUpdate(childComplexity, args["input"].(ImageTypeOrderInput)), true + case "Mutation.imageTypeSetEnabled": + if e.ComplexityRoot.Mutation.ImageTypeSetEnabled == nil { + break + } + + args, err := ec.field_Mutation_imageTypeSetEnabled_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.ComplexityRoot.Mutation.ImageTypeSetEnabled(childComplexity, args["input"].(ImageTypeEnabledInput)), true case "Mutation.markNotificationsRead": if e.ComplexityRoot.Mutation.MarkNotificationsRead == nil { break @@ -2502,6 +2849,17 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin } return e.ComplexityRoot.Mutation.UpdateEditComment(childComplexity, args["input"].(UpdateEditCommentInput)), true + case "Mutation.updateImageTypePreferences": + if e.ComplexityRoot.Mutation.UpdateImageTypePreferences == nil { + break + } + + args, err := ec.field_Mutation_updateImageTypePreferences_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.ComplexityRoot.Mutation.UpdateImageTypePreferences(childComplexity, args["input"].(ImageTypePreferencesInput)), true case "Mutation.updateNotificationSubscriptions": if e.ComplexityRoot.Mutation.UpdateNotificationSubscriptions == nil { break @@ -2802,6 +3160,18 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin } return e.ComplexityRoot.Performer.Tattoos(childComplexity), true + case "Performer.thumbnail": + if e.ComplexityRoot.Performer.Thumbnail == nil { + break + } + + return e.ComplexityRoot.Performer.Thumbnail(childComplexity), true + case "Performer.typed_images": + if e.ComplexityRoot.Performer.TypedImages == nil { + break + } + + return e.ComplexityRoot.Performer.TypedImages(childComplexity), true case "Performer.updated": if e.ComplexityRoot.Performer.Updated == nil { break @@ -3087,6 +3457,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin } return e.ComplexityRoot.PerformerEdit.HipSize(childComplexity), true + case "PerformerEdit.image_changes": + if e.ComplexityRoot.PerformerEdit.ImageChanges == nil { + break + } + + return e.ComplexityRoot.PerformerEdit.ImageChanges(childComplexity), true case "PerformerEdit.images": if e.ComplexityRoot.PerformerEdit.Images == nil { break @@ -3141,6 +3517,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin } return e.ComplexityRoot.PerformerEdit.Tattoos(childComplexity), true + case "PerformerEdit.typed_images": + if e.ComplexityRoot.PerformerEdit.TypedImages == nil { + break + } + + return e.ComplexityRoot.PerformerEdit.TypedImages(childComplexity), true case "PerformerEdit.urls": if e.ComplexityRoot.PerformerEdit.Urls == nil { break @@ -3403,6 +3785,17 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin } return e.ComplexityRoot.Query.GetUnreadNotificationCount(childComplexity), true + case "Query.imageTypeGroups": + if e.ComplexityRoot.Query.ImageTypeGroups == nil { + break + } + + args, err := ec.field_Query_imageTypeGroups_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.ComplexityRoot.Query.ImageTypeGroups(childComplexity, args["target"].(*ImageTypeScopeEnum), args["include_disabled"].(*bool)), true case "Query.me": if e.ComplexityRoot.Query.Me == nil { @@ -4575,6 +4968,25 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.TagEdit.RemovedAliases(childComplexity), true + case "TypedImage.date": + if e.ComplexityRoot.TypedImage.Date == nil { + break + } + + return e.ComplexityRoot.TypedImage.Date(childComplexity), true + case "TypedImage.image": + if e.ComplexityRoot.TypedImage.Image == nil { + break + } + + return e.ComplexityRoot.TypedImage.Image(childComplexity), true + case "TypedImage.types": + if e.ComplexityRoot.TypedImage.Types == nil { + break + } + + return e.ComplexityRoot.TypedImage.Types(childComplexity), true + case "URL.site": if e.ComplexityRoot.URL.Site == nil { break @@ -4650,6 +5062,18 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin } return e.ComplexityRoot.User.ID(childComplexity), true + case "User.image_type_group_preferences": + if e.ComplexityRoot.User.ImageTypeGroupPreferences == nil { + break + } + + return e.ComplexityRoot.User.ImageTypeGroupPreferences(childComplexity), true + case "User.image_type_preferences": + if e.ComplexityRoot.User.ImageTypePreferences == nil { + break + } + + return e.ComplexityRoot.User.ImageTypePreferences(childComplexity), true case "User.invite_codes": if e.ComplexityRoot.User.InviteCodes == nil { break @@ -4870,8 +5294,13 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputHairColorCriterionInput, ec.unmarshalInputHideEditCommentInput, ec.unmarshalInputIDCriterionInput, + ec.unmarshalInputImageAssignmentInput, ec.unmarshalInputImageCreateInput, + ec.unmarshalInputImageCropInput, ec.unmarshalInputImageDestroyInput, + ec.unmarshalInputImageTypeEnabledInput, + ec.unmarshalInputImageTypeOrderInput, + ec.unmarshalInputImageTypePreferencesInput, ec.unmarshalInputImageUpdateInput, ec.unmarshalInputIntCriterionInput, ec.unmarshalInputMarkNotificationReadInput, @@ -5351,6 +5780,38 @@ type Image { input ImageCreateInput { url: String file: Upload + crop: ImageCropInput +} + +""" +A frame to cut an upload down to, in the coordinates the client is looking at. + +Cropping happens here rather than in the browser for two reasons. A canvas +re-encode is a second lossy generation on top of whatever the contributor +started with, where the server decodes once and encodes once. And images are +deduplicated on a checksum of their stored bytes, which stops working if the +bytes are produced by whichever encoder the uploader's browser happens to +have: two people cropping the same source to the same frame would land as two +images +""" +input ImageCropInput { + """Distance from the left edge, as a fraction of the width""" + x: Float! + """Distance from the top edge, as a fraction of the height""" + y: Float! + """Fraction of the width to keep""" + width: Float! + """Fraction of the height to keep""" + height: Float! + """ + Degrees to rotate clockwise before cutting, for a tilted horizon. The frame + above is measured against the rotated image, which is larger than the + original - the same thing the client is dragging over. + + EXIF orientation is applied before any of this, so the coordinates are the + ones a browser shows rather than the ones stored in the file + """ + angle: Float = 0 } input ImageUpdateInput { @@ -5361,6 +5822,348 @@ input ImageUpdateInput { input ImageDestroyInput { id: ID! } +`, BuiltIn: false}, + {Name: "../../graphql/schema/types/image_type.graphql", Input: `"""A dimension of the image type vocabulary. Types within one group are ranked against each other.""" +enum ImageTypeGroupEnum { + SHOT + CROP + VIEW + POSTURE + DRESS +} + +""" +A label that may be applied to an image's presence on an entity. + +Every key is its group key followed by an underscore, so SHOT_PORTRAIT belongs +to the SHOT group. The vocabulary is fixed and identical on every instance, +which is what lets a client code against these values directly. +""" +enum ImageTypeEnum { + SHOT_PORTRAIT + SHOT_CANDID + SHOT_DETAIL + + CROP_FACE + CROP_BUST + CROP_THREE_QUARTER + CROP_THREE_QUARTER_PLUS + CROP_FULL_BODY + CROP_TORSO + CROP_WIDE + + VIEW_FRONT + VIEW_SIDE + VIEW_BACK + + POSTURE_STANDING + POSTURE_SITTING + POSTURE_KNEELING + POSTURE_SQUATTING + POSTURE_ON_ALL_FOURS + POSTURE_LYING + POSTURE_SUSPENDED + + DRESS_NON_NUDE + DRESS_UNDERWEAR + DRESS_TOPLESS + DRESS_NUDE + DRESS_EXPLICIT +} + +""" +The kinds of entity an image type may be applied to. + +Every value seeded today is PERFORMER-only. When scenes and studios get +image labelling, they get their own separate types and groups, not rows +here with SCENE or STUDIO added to a type's ` + "`" + `valid_types` + "`" + ` +""" +enum ImageTypeScopeEnum { + PERFORMER + SCENE + STUDIO +} + +type ImageTypeGroup { + key: ImageTypeGroupEnum! + name: String! + description: String + """Dimension priority when ranking images; lower wins""" + sort_order: Int! + """At most one type from this group may be assigned to an image""" + exclusive: Boolean! + """ + Whether this instance uses this dimension. A disabled group is not offered + when labelling and takes no part in ranking; existing assignments are kept, + so re-enabling restores them. + """ + enabled: Boolean! + types: [ImageType!]! +} + +""" +An image together with what it has been labelled on this entity. Not +performer-specific: scenes and studios expose the same type. +""" +type TypedImage { + image: Image! + types: [ImageTypeEnum!]! + """When the image is from. Partial ISO 8601: 2019, 2019-06, or 2019-06-15.""" + date: String +} + +""" +What an edit changes about one image's labels and date, grouped by image +rather than listed as flat added/removed tuples: one performer edit can +relabel a whole gallery. +""" +type ImageAssignmentChange { + image: Image! + added_types: [ImageTypeEnum!]! + removed_types: [ImageTypeEnum!]! + """ + The date this edit sets. Only meaningful when date_changed is true, where a + null means the edit clears the date. + """ + date: String + """Whether this edit changes the image's date at all.""" + date_changed: Boolean! +} + +""" +Everything said about one image's presence on an entity. An entry whose types +are empty clears that image's labels. + +**What each way of sending ` + "`" + `image_types` + "`" + ` means.** Three write paths implement +this: ` + "`" + `performerCreate` + "`" + ` and ` + "`" + `performerUpdate` + "`" + ` in Go, and the edit path in Go +at submission and SQL at apply. Nothing makes them agree but this table. + +| ` + "`" + `image_types` + "`" + ` is | performerCreate | performerUpdate | edit | +|---|---|---|---| +| absent | unlabelled | preserves all | preserves all | +| explicit ` + "`" + `null` + "`" + ` | unlabelled | **preserves all** | **clears all** | +| ` + "`" + `[]` + "`" + ` | unlabelled | clears all | clears all | +| non-empty | labels the images named | authoritative only over the images named | authoritative only over the images named | + +Null differs because the edit path is told which fields the client stated and +` + "`" + `performerUpdate` + "`" + ` is not. **Send ` + "`" + `[]` + "`" + ` to clear on any path** and the question +does not arise. + +Note that a non-empty list leaves an image it does not mention exactly as it +was; otherwise every client touching ` + "`" + `image_ids` + "`" + ` would have to restate the +whole gallery's labels or destroy them. + +**And the same for ` + "`" + `date` + "`" + `,** which is single-valued and so overrides +rather than merges: + +| the submission | the image's date | +|---|---| +| no entry for this image | kept | +| an entry stating ` + "`" + `date` + "`" + ` | set | +| an entry omitting ` + "`" + `date` + "`" + ` | cleared, see the field's own note | +| an entry omitting it, on an image being added | stays empty, and is not reported as a change | +""" +input ImageAssignmentInput { + image_id: ID! + types: [ImageTypeEnum!]! + """ + When the image is from. Partial ISO 8601: 2019, 2019-06, or 2019-06-15. + + An entry states the whole of what is true about its image, so omitting this + clears the date rather than leaving it. Send the current value back if the + change is only to the labels. + """ + date: String +} + +""" +A complete reordering of the vocabulary. Partial lists are rejected rather than +merged. +""" +input ImageTypeOrderInput { + """Groups in priority order. Must list every group exactly once.""" + groups: [ImageTypeGroupEnum!]! + """ + Types in priority order. Must list every type exactly once. Only position + within each group counts, so types of different groups may interleave freely. + """ + types: [ImageTypeEnum!]! +} + +""" +One user's ranking. Unlike the admin ordering both lists may be partial: a user +says what they care about and everything else keeps the instance order behind +it, which is what lets someone express "nudes first" without having to rank all +seventeen types. +""" +input ImageTypePreferencesInput { + """Types in preferred order, position within each group being what counts.""" + types: [ImageTypeEnum!]! + """ + Groups in preferred order, deciding which dimension is compared first. + + Absent leaves the group preference as it is; an empty list clears it. Not + defaulted, so a client sending only ` + "`" + `types` + "`" + ` keeps the group ordering it did + not mention. + """ + groups: [ImageTypeGroupEnum!] +} + +type ImageType { + key: ImageTypeEnum! + name: String! + description: String + """Value priority within the group; lower wins""" + sort_order: Int! + valid_types: [ImageTypeScopeEnum!]! + """Whether this instance uses this type. Disabled types cannot be assigned.""" + enabled: Boolean! + """ + Types this one cannot share an image with, across groups: a face crop cannot + be topless, because the chest is not in frame. Symmetric: each side + of a pair lists the other. Assigning both is rejected; a client should stop + offering the second once the first is chosen. + """ + conflicts_with: [ImageTypeEnum!]! + """ + The frame to crop to for this type, or null if the instance has no template + for it. Only crops have one - nothing about a pose or a state of dress says + anything about the shape of the picture + """ + crop_template: CropTemplate +} + +""" +A crop frame, read from a Photoshop template + +The template file is the source of truth: the guides drawn over the cropping +tool and the .psd a contributor can download for their own editor are the same +bytes, so the two cannot drift +""" +type CropTemplate { + """ + Width over height, taken from the template's canvas rather than set + anywhere + """ + aspect_ratio: Float! + guides: [CropGuide!]! + """ + Outlines drawn on the template's own layers like an oval for a face to sit + inside, a bar marking a margin. Guidance only: the crop is still a + rectangle, and nothing here changes what the server cuts + """ + shapes: [CropShape!]! +} + +"""One outline drawn in a crop template""" +type CropShape { + """ + What the template's author called the layer, like "head guide", "eyes soft + anchor", or null for an unnamed layer + """ + label: String + subpaths: [CropSubpath!]! +} + +""" +One continuous run of a shape's outline + +A shape can be several: a ring is an outer subpath and an inner one, and +whether each closes back on itself is the difference between an outline and an +arc +""" +type CropSubpath { + closed: Boolean! + knots: [CropKnot!]! +} + +""" +One anchor of an outline, with the control point either side of it + +Every segment is a cubic curve, including straight ones. Photoshop draws a +straight edge as a curve whose controls sit on its anchors, so a rectangle and +an ellipse arrive in the same shape +""" +type CropKnot { + """The control point governing the curve arriving at this anchor""" + control_in: CropPoint! + anchor: CropPoint! + """The control point governing the curve leaving it""" + control_out: CropPoint! +} + +""" +A position on the template's canvas, as fractions of its width and height + +Fractions like a guide's position, and for the same reason: a template is drawn +at one size and rendered at every other. Values outside 0 to 1 are legitimate: +a crop box is often drawn a hair outside the canvas so its stroke does not eat +into the picture +""" +type CropPoint { + x: Float! + y: Float! +} + +"""One guide line of a crop template""" +type CropGuide { + axis: CropGuideAxisEnum! + """ + Where the line sits, as a fraction of the canvas along its axis: 0 is the + left or top edge, 1 the right or bottom. A fraction rather than a pixel + because a template is drawn at one size and rendered at every other + """ + position: Float! + """ + How closely the line is meant to be followed, where the template says. An + anchor is meant to be hit; a reference is for judgement and balance + """ + role: CropGuideRoleEnum + """ + What the line is for, like "bisects the eyes", "where the thighs meet", or + null when the template does not name it + """ + label: String + """ + Whether a frame is resized around this line when the contributor holds + Shift + + Independent of ` + "`" + `role` + "`" + `, which says how closely a line is meant to be + followed. A headshot's eye line is the softest line in its template (like the + head and chin can be hard limits) and is still the right thing to turn a + resize about, so the two cannot be the same field + + At most one guide per axis carries it. A template naming none on an axis + resizes about the centre there + """ + pivot: Boolean! +} + +enum CropGuideAxisEnum { + """A vertical line, positioned across the width""" + X + """A horizontal line, positioned down the height""" + Y +} + +enum CropGuideRoleEnum { + ANCHOR + REFERENCE + MARGIN +} + +""" +Which parts of the vocabulary an instance switches off. + +Expressed as what is disabled rather than what is enabled, so a type added to +the taxonomy later arrives switched on. +""" +input ImageTypeEnabledInput { + """Groups to switch off. A group being off implies its types are too.""" + disabled_groups: [ImageTypeGroupEnum!]! = [] + """Types to switch off individually, whatever their group's state.""" + disabled_types: [ImageTypeEnum!]! = [] +} `, BuiltIn: false}, {Name: "../../graphql/schema/types/misc.graphql", Input: `scalar Date scalar DateTime @@ -5650,7 +6453,23 @@ type Performer { career_end_year: Int tattoos: [BodyModification!] piercings: [BodyModification!] + """ + The gallery, ordered as this viewer ranks image types. Anywhere one image + stands for the performer, that is ` + "`" + `images[0]` + "`" + `: a card, a grid, a merge + target. + """ images: [Image!]! + """The same images, each with the types it has been labelled with here""" + typed_images: [TypedImage!]! + """ + The most recognisable image, for search results and dropdowns only. + + Always prefers a face crop and ignores the viewer's type preference: + legibility at thumbnail size is not a matter of taste, and being the same + for everyone is what lets it be cached. Everywhere else wants ` + "`" + `images[0]` + "`" + `, + which does follow the viewer. + """ + thumbnail: Image deleted: Boolean! edits: [Edit!]! scene_count: Int! @@ -5706,6 +6525,11 @@ input PerformerCreateInput { tattoos: [BodyModificationInput!] piercings: [BodyModificationInput!] image_ids: [ID!] + """ + Labels for the images named. An image in image_ids with no entry here is + simply unlabelled; there is nothing to preserve on a create. + """ + image_types: [ImageAssignmentInput!] draft_id: ID } @@ -5733,6 +6557,17 @@ input PerformerUpdateInput { tattoos: [BodyModificationInput!] piercings: [BodyModificationInput!] image_ids: [ID!] + """ + Labels for the images named. Absent leaves every assignment untouched, an + empty list clears them all, and an image in image_ids with no entry here + keeps what it has. + + Explicit null behaves as absent and preserves, which differs from the edit + path, where it clears. This path is not told which fields the client stated, + so it cannot tell an omitted field from one set to null; the edit path is, + and does. Send an empty list to clear, on either path. + """ + image_types: [ImageAssignmentInput!] } input PerformerDestroyInput { @@ -5762,6 +6597,16 @@ input PerformerEditDetailsInput { tattoos: [BodyModificationInput!] piercings: [BodyModificationInput!] image_ids: [ID!] + """ + Labels for the images named. Omitting the field leaves assignments alone; + null or an empty list clears them all, matching image_ids. A non-empty list + is authoritative only over the images it names. + + Null clearing here and preserving on performerUpdate is not a rule, it is + what each path can see: this one is told which fields the client stated, and + that one is not. Send an empty list to clear, on either path. + """ + image_types: [ImageAssignmentInput!] draft_id: ID } @@ -5809,6 +6654,17 @@ type PerformerEdit { removed_piercings: [BodyModification!] added_images: [Image!] removed_images: [Image!] + """Label and date changes, one entry per affected image""" + image_changes: [ImageAssignmentChange!]! + """ + The gallery this edit results in: each surviving image with the labels and + date it will carry once applied. + + The state being voted on, as opposed to image_changes, which is what moves. + A reviewer opening an image wants to see what it will be, the same way the + gallery lightbox shows it. + """ + typed_images: [TypedImage!]! draft_id: ID aliases: [String!]! @@ -6652,6 +7508,10 @@ type User { """Should not be visible to other users""" api_key: String @isUserOwner notification_subscriptions: [NotificationEnum!]! @isUserOwner + """Preferred order of types within their group, when ranking images. Empty means no preference.""" + image_type_preferences: [ImageTypeEnum!]! @isUserOwner + """Preferred order of the groups themselves, deciding which dimension is compared first. Empty means the instance order.""" + image_type_group_preferences: [ImageTypeGroupEnum!]! @isUserOwner """ Vote counts by type """ vote_count: UserVoteCount! @@ -6889,6 +7749,19 @@ type Query { """Discover favicon candidates for a URL, returned as base64 data URLs""" fetchSiteFavicons(url: String!): [SiteFavicon!]! @hasRole(role: ADMIN) + #### Image types #### + + """ + The image type vocabulary, groups in priority order with their types nested. + Filtering by target drops types that entity kind cannot carry, and drops any + group thereby left empty. + + Disabled groups and types are omitted unless asked for: a labeller should not + see what the instance has switched off, but the admin who switched it off has + to be able to switch it back on. + """ + imageTypeGroups(target: ImageTypeScopeEnum, include_disabled: Boolean = false): [ImageTypeGroup!]! @hasRole(role: READ) + #### Edits #### findEdit(id: ID!): Edit @hasRole(role: READ) @@ -6989,6 +7862,20 @@ type Mutation { siteCategoryUpdate(input: SiteCategoryUpdateInput!): SiteCategory @hasRole(role: ADMIN) siteCategoryDestroy(input: SiteCategoryDestroyInput!): Boolean! @hasRole(role: ADMIN) + """ + Reorder the image type vocabulary, deciding which image ranks first + instance-wide. Both lists must be complete; returns the reordered vocabulary. + """ + imageTypeOrderUpdate(input: ImageTypeOrderInput!): [ImageTypeGroup!]! @hasRole(role: ADMIN) + + """ + Choose which of the vocabulary this instance uses. Takes the complete set of + keys to switch off, so anything absent is on; returns the whole vocabulary, + disabled entries included. Nothing is deleted, so switching a group back on + restores every label made while it was in use. + """ + imageTypeSetEnabled(input: ImageTypeEnabledInput!): [ImageTypeGroup!]! @hasRole(role: ADMIN) + """Regenerates the api key for the given user, or the current user if id not provided""" regenerateAPIKey(userID: ID): String! @@ -7063,6 +7950,13 @@ type Mutation { markNotificationsRead(notification: MarkNotificationReadInput): Boolean! @hasRole(role: READ) """Update notification subscriptions for current user.""" updateNotificationSubscriptions(subscriptions: [NotificationEnum!]!): Boolean! @hasRole(role: READ) + + """ + Reorder image types for the current user, and optionally the groups they sit + in. Unlike the admin ordering both lists may be partial: anything left out + trails what was listed, in instance order. Empty lists clear that preference. + """ + updateImageTypePreferences(input: ImageTypePreferencesInput!): Boolean! @hasRole(role: READ) } schema { @@ -7125,6 +8019,76 @@ func (ec *executionContext) childFields_ClusterSceneSubmission(ctx context.Conte return nil, fmt.Errorf("no field named %q was found under type ClusterSceneSubmission", field.Name) } +func (ec *executionContext) childFields_CropGuide(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "axis": + return ec.fieldContext_CropGuide_axis(ctx, field) + case "position": + return ec.fieldContext_CropGuide_position(ctx, field) + case "role": + return ec.fieldContext_CropGuide_role(ctx, field) + case "label": + return ec.fieldContext_CropGuide_label(ctx, field) + case "pivot": + return ec.fieldContext_CropGuide_pivot(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type CropGuide", field.Name) +} + +func (ec *executionContext) childFields_CropKnot(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "control_in": + return ec.fieldContext_CropKnot_control_in(ctx, field) + case "anchor": + return ec.fieldContext_CropKnot_anchor(ctx, field) + case "control_out": + return ec.fieldContext_CropKnot_control_out(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type CropKnot", field.Name) +} + +func (ec *executionContext) childFields_CropPoint(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "x": + return ec.fieldContext_CropPoint_x(ctx, field) + case "y": + return ec.fieldContext_CropPoint_y(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type CropPoint", field.Name) +} + +func (ec *executionContext) childFields_CropShape(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "label": + return ec.fieldContext_CropShape_label(ctx, field) + case "subpaths": + return ec.fieldContext_CropShape_subpaths(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type CropShape", field.Name) +} + +func (ec *executionContext) childFields_CropSubpath(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "closed": + return ec.fieldContext_CropSubpath_closed(ctx, field) + case "knots": + return ec.fieldContext_CropSubpath_knots(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type CropSubpath", field.Name) +} + +func (ec *executionContext) childFields_CropTemplate(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "aspect_ratio": + return ec.fieldContext_CropTemplate_aspect_ratio(ctx, field) + case "guides": + return ec.fieldContext_CropTemplate_guides(ctx, field) + case "shapes": + return ec.fieldContext_CropTemplate_shapes(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type CropTemplate", field.Name) +} + func (ec *executionContext) childFields_Draft(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "id": @@ -7341,6 +8305,64 @@ func (ec *executionContext) childFields_Image(ctx context.Context, field graphql return nil, fmt.Errorf("no field named %q was found under type Image", field.Name) } +func (ec *executionContext) childFields_ImageAssignmentChange(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "image": + return ec.fieldContext_ImageAssignmentChange_image(ctx, field) + case "added_types": + return ec.fieldContext_ImageAssignmentChange_added_types(ctx, field) + case "removed_types": + return ec.fieldContext_ImageAssignmentChange_removed_types(ctx, field) + case "date": + return ec.fieldContext_ImageAssignmentChange_date(ctx, field) + case "date_changed": + return ec.fieldContext_ImageAssignmentChange_date_changed(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ImageAssignmentChange", field.Name) +} + +func (ec *executionContext) childFields_ImageType(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "key": + return ec.fieldContext_ImageType_key(ctx, field) + case "name": + return ec.fieldContext_ImageType_name(ctx, field) + case "description": + return ec.fieldContext_ImageType_description(ctx, field) + case "sort_order": + return ec.fieldContext_ImageType_sort_order(ctx, field) + case "valid_types": + return ec.fieldContext_ImageType_valid_types(ctx, field) + case "enabled": + return ec.fieldContext_ImageType_enabled(ctx, field) + case "conflicts_with": + return ec.fieldContext_ImageType_conflicts_with(ctx, field) + case "crop_template": + return ec.fieldContext_ImageType_crop_template(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ImageType", field.Name) +} + +func (ec *executionContext) childFields_ImageTypeGroup(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "key": + return ec.fieldContext_ImageTypeGroup_key(ctx, field) + case "name": + return ec.fieldContext_ImageTypeGroup_name(ctx, field) + case "description": + return ec.fieldContext_ImageTypeGroup_description(ctx, field) + case "sort_order": + return ec.fieldContext_ImageTypeGroup_sort_order(ctx, field) + case "exclusive": + return ec.fieldContext_ImageTypeGroup_exclusive(ctx, field) + case "enabled": + return ec.fieldContext_ImageTypeGroup_enabled(ctx, field) + case "types": + return ec.fieldContext_ImageTypeGroup_types(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ImageTypeGroup", field.Name) +} + func (ec *executionContext) childFields_InviteKey(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "id": @@ -7457,6 +8479,10 @@ func (ec *executionContext) childFields_Performer(ctx context.Context, field gra return ec.fieldContext_Performer_piercings(ctx, field) case "images": return ec.fieldContext_Performer_images(ctx, field) + case "typed_images": + return ec.fieldContext_Performer_typed_images(ctx, field) + case "thumbnail": + return ec.fieldContext_Performer_thumbnail(ctx, field) case "deleted": return ec.fieldContext_Performer_deleted(ctx, field) case "edits": @@ -7849,6 +8875,18 @@ func (ec *executionContext) childFields_TagCategory(ctx context.Context, field g return nil, fmt.Errorf("no field named %q was found under type TagCategory", field.Name) } +func (ec *executionContext) childFields_TypedImage(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "image": + return ec.fieldContext_TypedImage_image(ctx, field) + case "types": + return ec.fieldContext_TypedImage_types(ctx, field) + case "date": + return ec.fieldContext_TypedImage_date(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TypedImage", field.Name) +} + func (ec *executionContext) childFields_URL(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "url": @@ -7885,6 +8923,10 @@ func (ec *executionContext) childFields_User(ctx context.Context, field graphql. return ec.fieldContext_User_api_key(ctx, field) case "notification_subscriptions": return ec.fieldContext_User_notification_subscriptions(ctx, field) + case "image_type_preferences": + return ec.fieldContext_User_image_type_preferences(ctx, field) + case "image_type_group_preferences": + return ec.fieldContext_User_image_type_group_preferences(ctx, field) case "vote_count": return ec.fieldContext_User_vote_count(ctx, field) case "edit_count": @@ -8351,6 +9393,34 @@ func (ec *executionContext) field_Mutation_imageDestroy_args(ctx context.Context return args, nil } +func (ec *executionContext) field_Mutation_imageTypeOrderUpdate_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", + func(ctx context.Context, v any) (ImageTypeOrderInput, error) { + return ec.unmarshalNImageTypeOrderInput2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeOrderInput(ctx, v) + }) + if err != nil { + return nil, err + } + args["input"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Mutation_imageTypeSetEnabled_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", + func(ctx context.Context, v any) (ImageTypeEnabledInput, error) { + return ec.unmarshalNImageTypeEnabledInput2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnabledInput(ctx, v) + }) + if err != nil { + return nil, err + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_markNotificationsRead_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -8971,6 +10041,20 @@ func (ec *executionContext) field_Mutation_updateEditComment_args(ctx context.Co return args, nil } +func (ec *executionContext) field_Mutation_updateImageTypePreferences_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", + func(ctx context.Context, v any) (ImageTypePreferencesInput, error) { + return ec.unmarshalNImageTypePreferencesInput2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypePreferencesInput(ctx, v) + }) + if err != nil { + return nil, err + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_updateNotificationSubscriptions_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -9381,6 +10465,28 @@ func (ec *executionContext) field_Query_fingerprintClusters_args(ctx context.Con return args, nil } +func (ec *executionContext) field_Query_imageTypeGroups_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "target", + func(ctx context.Context, v any) (*ImageTypeScopeEnum, error) { + return ec.unmarshalOImageTypeScopeEnum2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeScopeEnum(ctx, v) + }) + if err != nil { + return nil, err + } + args["target"] = arg0 + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "include_disabled", + func(ctx context.Context, v any) (*bool, error) { + return ec.unmarshalOBoolean2ᚖbool(ctx, v) + }) + if err != nil { + return nil, err + } + args["include_disabled"] = arg1 + return args, nil +} + func (ec *executionContext) field_Query_queryEdits_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -10207,6 +11313,460 @@ func (ec *executionContext) fieldContext_CommentVotedEdit_comment(_ context.Cont return fc, nil } +func (ec *executionContext) _CropGuide_axis(ctx context.Context, field graphql.CollectedField, obj *CropGuide) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CropGuide_axis(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Axis, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v CropGuideAxisEnum) graphql.Marshaler { + return ec.marshalNCropGuideAxisEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropGuideAxisEnum(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CropGuide_axis(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("CropGuide", field, false, false, errors.New("field of type CropGuideAxisEnum does not have child fields")) +} + +func (ec *executionContext) _CropGuide_position(ctx context.Context, field graphql.CollectedField, obj *CropGuide) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CropGuide_position(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Position, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v float64) graphql.Marshaler { + return ec.marshalNFloat2float64(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CropGuide_position(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("CropGuide", field, false, false, errors.New("field of type Float does not have child fields")) +} + +func (ec *executionContext) _CropGuide_role(ctx context.Context, field graphql.CollectedField, obj *CropGuide) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CropGuide_role(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Role, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *CropGuideRoleEnum) graphql.Marshaler { + return ec.marshalOCropGuideRoleEnum2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropGuideRoleEnum(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_CropGuide_role(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("CropGuide", field, false, false, errors.New("field of type CropGuideRoleEnum does not have child fields")) +} + +func (ec *executionContext) _CropGuide_label(ctx context.Context, field graphql.CollectedField, obj *CropGuide) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CropGuide_label(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Label, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_CropGuide_label(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("CropGuide", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _CropGuide_pivot(ctx context.Context, field graphql.CollectedField, obj *CropGuide) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CropGuide_pivot(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Pivot, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CropGuide_pivot(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("CropGuide", field, false, false, errors.New("field of type Boolean does not have child fields")) +} + +func (ec *executionContext) _CropKnot_control_in(ctx context.Context, field graphql.CollectedField, obj *CropKnot) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CropKnot_control_in(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ControlIn, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *CropPoint) graphql.Marshaler { + return ec.marshalNCropPoint2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropPoint(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CropKnot_control_in(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CropKnot", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_CropPoint(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _CropKnot_anchor(ctx context.Context, field graphql.CollectedField, obj *CropKnot) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CropKnot_anchor(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Anchor, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *CropPoint) graphql.Marshaler { + return ec.marshalNCropPoint2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropPoint(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CropKnot_anchor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CropKnot", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_CropPoint(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _CropKnot_control_out(ctx context.Context, field graphql.CollectedField, obj *CropKnot) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CropKnot_control_out(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ControlOut, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *CropPoint) graphql.Marshaler { + return ec.marshalNCropPoint2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropPoint(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CropKnot_control_out(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CropKnot", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_CropPoint(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _CropPoint_x(ctx context.Context, field graphql.CollectedField, obj *CropPoint) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CropPoint_x(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.X, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v float64) graphql.Marshaler { + return ec.marshalNFloat2float64(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CropPoint_x(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("CropPoint", field, false, false, errors.New("field of type Float does not have child fields")) +} + +func (ec *executionContext) _CropPoint_y(ctx context.Context, field graphql.CollectedField, obj *CropPoint) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CropPoint_y(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Y, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v float64) graphql.Marshaler { + return ec.marshalNFloat2float64(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CropPoint_y(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("CropPoint", field, false, false, errors.New("field of type Float does not have child fields")) +} + +func (ec *executionContext) _CropShape_label(ctx context.Context, field graphql.CollectedField, obj *CropShape) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CropShape_label(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Label, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_CropShape_label(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("CropShape", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _CropShape_subpaths(ctx context.Context, field graphql.CollectedField, obj *CropShape) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CropShape_subpaths(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Subpaths, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []CropSubpath) graphql.Marshaler { + return ec.marshalNCropSubpath2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropSubpathᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CropShape_subpaths(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CropShape", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_CropSubpath(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _CropSubpath_closed(ctx context.Context, field graphql.CollectedField, obj *CropSubpath) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CropSubpath_closed(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Closed, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CropSubpath_closed(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("CropSubpath", field, false, false, errors.New("field of type Boolean does not have child fields")) +} + +func (ec *executionContext) _CropSubpath_knots(ctx context.Context, field graphql.CollectedField, obj *CropSubpath) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CropSubpath_knots(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Knots, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []CropKnot) graphql.Marshaler { + return ec.marshalNCropKnot2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropKnotᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CropSubpath_knots(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CropSubpath", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_CropKnot(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _CropTemplate_aspect_ratio(ctx context.Context, field graphql.CollectedField, obj *CropTemplate) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CropTemplate_aspect_ratio(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.AspectRatio, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v float64) graphql.Marshaler { + return ec.marshalNFloat2float64(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CropTemplate_aspect_ratio(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("CropTemplate", field, false, false, errors.New("field of type Float does not have child fields")) +} + +func (ec *executionContext) _CropTemplate_guides(ctx context.Context, field graphql.CollectedField, obj *CropTemplate) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CropTemplate_guides(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Guides, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []CropGuide) graphql.Marshaler { + return ec.marshalNCropGuide2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropGuideᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CropTemplate_guides(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CropTemplate", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_CropGuide(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _CropTemplate_shapes(ctx context.Context, field graphql.CollectedField, obj *CropTemplate) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CropTemplate_shapes(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Shapes, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []CropShape) graphql.Marshaler { + return ec.marshalNCropShape2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropShapeᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CropTemplate_shapes(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CropTemplate", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_CropShape(ctx, field) + }, + } + return fc, nil +} + func (ec *executionContext) _DownvoteOwnEdit_edit(ctx context.Context, field graphql.CollectedField, obj *DownvoteOwnEdit) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -12163,6 +13723,493 @@ func (ec *executionContext) fieldContext_Image_height(_ context.Context, field g return graphql.NewScalarFieldContext("Image", field, false, false, errors.New("field of type Int does not have child fields")) } +func (ec *executionContext) _ImageAssignmentChange_image(ctx context.Context, field graphql.CollectedField, obj *ImageAssignmentChange) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageAssignmentChange_image(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Image, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *Image) graphql.Marshaler { + return ec.marshalNImage2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImage(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageAssignmentChange_image(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ImageAssignmentChange", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_Image(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _ImageAssignmentChange_added_types(ctx context.Context, field graphql.CollectedField, obj *ImageAssignmentChange) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageAssignmentChange_added_types(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.AddedTypes, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []ImageTypeEnum) graphql.Marshaler { + return ec.marshalNImageTypeEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnumᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageAssignmentChange_added_types(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageAssignmentChange", field, false, false, errors.New("field of type ImageTypeEnum does not have child fields")) +} + +func (ec *executionContext) _ImageAssignmentChange_removed_types(ctx context.Context, field graphql.CollectedField, obj *ImageAssignmentChange) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageAssignmentChange_removed_types(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.RemovedTypes, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []ImageTypeEnum) graphql.Marshaler { + return ec.marshalNImageTypeEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnumᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageAssignmentChange_removed_types(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageAssignmentChange", field, false, false, errors.New("field of type ImageTypeEnum does not have child fields")) +} + +func (ec *executionContext) _ImageAssignmentChange_date(ctx context.Context, field graphql.CollectedField, obj *ImageAssignmentChange) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageAssignmentChange_date(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Date, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_ImageAssignmentChange_date(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageAssignmentChange", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _ImageAssignmentChange_date_changed(ctx context.Context, field graphql.CollectedField, obj *ImageAssignmentChange) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageAssignmentChange_date_changed(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.DateChanged, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageAssignmentChange_date_changed(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageAssignmentChange", field, false, false, errors.New("field of type Boolean does not have child fields")) +} + +func (ec *executionContext) _ImageType_key(ctx context.Context, field graphql.CollectedField, obj *ImageType) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageType_key(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Key, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v ImageTypeEnum) graphql.Marshaler { + return ec.marshalNImageTypeEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnum(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageType_key(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageType", field, false, false, errors.New("field of type ImageTypeEnum does not have child fields")) +} + +func (ec *executionContext) _ImageType_name(ctx context.Context, field graphql.CollectedField, obj *ImageType) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageType_name(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Name, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageType_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageType", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _ImageType_description(ctx context.Context, field graphql.CollectedField, obj *ImageType) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageType_description(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Description, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_ImageType_description(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageType", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _ImageType_sort_order(ctx context.Context, field graphql.CollectedField, obj *ImageType) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageType_sort_order(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.SortOrder, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageType_sort_order(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageType", field, false, false, errors.New("field of type Int does not have child fields")) +} + +func (ec *executionContext) _ImageType_valid_types(ctx context.Context, field graphql.CollectedField, obj *ImageType) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageType_valid_types(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ValidTypes, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []ImageTypeScopeEnum) graphql.Marshaler { + return ec.marshalNImageTypeScopeEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeScopeEnumᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageType_valid_types(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageType", field, false, false, errors.New("field of type ImageTypeScopeEnum does not have child fields")) +} + +func (ec *executionContext) _ImageType_enabled(ctx context.Context, field graphql.CollectedField, obj *ImageType) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageType_enabled(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Enabled, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageType_enabled(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageType", field, false, false, errors.New("field of type Boolean does not have child fields")) +} + +func (ec *executionContext) _ImageType_conflicts_with(ctx context.Context, field graphql.CollectedField, obj *ImageType) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageType_conflicts_with(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ConflictsWith, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []ImageTypeEnum) graphql.Marshaler { + return ec.marshalNImageTypeEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnumᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageType_conflicts_with(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageType", field, false, false, errors.New("field of type ImageTypeEnum does not have child fields")) +} + +func (ec *executionContext) _ImageType_crop_template(ctx context.Context, field graphql.CollectedField, obj *ImageType) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageType_crop_template(ctx, field) + }, + func(ctx context.Context) (any, error) { + return ec.Resolvers.ImageType().CropTemplate(ctx, obj) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *CropTemplate) graphql.Marshaler { + return ec.marshalOCropTemplate2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropTemplate(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_ImageType_crop_template(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ImageType", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_CropTemplate(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _ImageTypeGroup_key(ctx context.Context, field graphql.CollectedField, obj *ImageTypeGroup) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageTypeGroup_key(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Key, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v ImageTypeGroupEnum) graphql.Marshaler { + return ec.marshalNImageTypeGroupEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupEnum(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageTypeGroup_key(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageTypeGroup", field, false, false, errors.New("field of type ImageTypeGroupEnum does not have child fields")) +} + +func (ec *executionContext) _ImageTypeGroup_name(ctx context.Context, field graphql.CollectedField, obj *ImageTypeGroup) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageTypeGroup_name(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Name, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageTypeGroup_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageTypeGroup", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _ImageTypeGroup_description(ctx context.Context, field graphql.CollectedField, obj *ImageTypeGroup) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageTypeGroup_description(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Description, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_ImageTypeGroup_description(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageTypeGroup", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _ImageTypeGroup_sort_order(ctx context.Context, field graphql.CollectedField, obj *ImageTypeGroup) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageTypeGroup_sort_order(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.SortOrder, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageTypeGroup_sort_order(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageTypeGroup", field, false, false, errors.New("field of type Int does not have child fields")) +} + +func (ec *executionContext) _ImageTypeGroup_exclusive(ctx context.Context, field graphql.CollectedField, obj *ImageTypeGroup) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageTypeGroup_exclusive(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Exclusive, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageTypeGroup_exclusive(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageTypeGroup", field, false, false, errors.New("field of type Boolean does not have child fields")) +} + +func (ec *executionContext) _ImageTypeGroup_enabled(ctx context.Context, field graphql.CollectedField, obj *ImageTypeGroup) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageTypeGroup_enabled(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Enabled, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageTypeGroup_enabled(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("ImageTypeGroup", field, false, false, errors.New("field of type Boolean does not have child fields")) +} + +func (ec *executionContext) _ImageTypeGroup_types(ctx context.Context, field graphql.CollectedField, obj *ImageTypeGroup) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_ImageTypeGroup_types(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Types, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []ImageType) graphql.Marshaler { + return ec.marshalNImageType2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_ImageTypeGroup_types(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ImageTypeGroup", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_ImageType(ctx, field) + }, + } + return fc, nil +} + func (ec *executionContext) _InviteKey_id(ctx context.Context, field graphql.CollectedField, obj *InviteKey) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -14452,6 +16499,130 @@ func (ec *executionContext) fieldContext_Mutation_siteCategoryDestroy(ctx contex return fc, nil } +func (ec *executionContext) _Mutation_imageTypeOrderUpdate(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Mutation_imageTypeOrderUpdate(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.Mutation().ImageTypeOrderUpdate(ctx, fc.Args["input"].(ImageTypeOrderInput)) + }, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "ADMIN") + if err != nil { + var zeroVal []ImageTypeGroup + return zeroVal, err + } + if ec.Directives.HasRole == nil { + var zeroVal []ImageTypeGroup + return zeroVal, errors.New("directive hasRole is not implemented") + } + return ec.Directives.HasRole(ctx, nil, directive0, role) + } + + next = directive1 + return next + }, + func(ctx context.Context, selections ast.SelectionSet, v []ImageTypeGroup) graphql.Marshaler { + return ec.marshalNImageTypeGroup2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Mutation_imageTypeOrderUpdate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_ImageTypeGroup(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_imageTypeOrderUpdate_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_imageTypeSetEnabled(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Mutation_imageTypeSetEnabled(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.Mutation().ImageTypeSetEnabled(ctx, fc.Args["input"].(ImageTypeEnabledInput)) + }, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "ADMIN") + if err != nil { + var zeroVal []ImageTypeGroup + return zeroVal, err + } + if ec.Directives.HasRole == nil { + var zeroVal []ImageTypeGroup + return zeroVal, errors.New("directive hasRole is not implemented") + } + return ec.Directives.HasRole(ctx, nil, directive0, role) + } + + next = directive1 + return next + }, + func(ctx context.Context, selections ast.SelectionSet, v []ImageTypeGroup) graphql.Marshaler { + return ec.marshalNImageTypeGroup2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Mutation_imageTypeSetEnabled(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_ImageTypeGroup(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_imageTypeSetEnabled_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Mutation_regenerateAPIKey(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -14849,7 +17020,379 @@ func (ec *executionContext) _Mutation_performerEdit(ctx context.Context, field g true, ) } -func (ec *executionContext) fieldContext_Mutation_performerEdit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_performerEdit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_Edit(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_performerEdit_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_studioEdit(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Mutation_studioEdit(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.Mutation().StudioEdit(ctx, fc.Args["input"].(StudioEditInput)) + }, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") + if err != nil { + var zeroVal *Edit + return zeroVal, err + } + if ec.Directives.HasRole == nil { + var zeroVal *Edit + return zeroVal, errors.New("directive hasRole is not implemented") + } + return ec.Directives.HasRole(ctx, nil, directive0, role) + } + + next = directive1 + return next + }, + func(ctx context.Context, selections ast.SelectionSet, v *Edit) graphql.Marshaler { + return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐEdit(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Mutation_studioEdit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_Edit(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_studioEdit_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_tagEdit(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Mutation_tagEdit(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.Mutation().TagEdit(ctx, fc.Args["input"].(TagEditInput)) + }, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") + if err != nil { + var zeroVal *Edit + return zeroVal, err + } + if ec.Directives.HasRole == nil { + var zeroVal *Edit + return zeroVal, errors.New("directive hasRole is not implemented") + } + return ec.Directives.HasRole(ctx, nil, directive0, role) + } + + next = directive1 + return next + }, + func(ctx context.Context, selections ast.SelectionSet, v *Edit) graphql.Marshaler { + return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐEdit(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Mutation_tagEdit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_Edit(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_tagEdit_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_sceneEditUpdate(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Mutation_sceneEditUpdate(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.Mutation().SceneEditUpdate(ctx, fc.Args["id"].(uuid.UUID), fc.Args["input"].(SceneEditInput)) + }, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") + if err != nil { + var zeroVal *Edit + return zeroVal, err + } + if ec.Directives.HasRole == nil { + var zeroVal *Edit + return zeroVal, errors.New("directive hasRole is not implemented") + } + return ec.Directives.HasRole(ctx, nil, directive0, role) + } + + next = directive1 + return next + }, + func(ctx context.Context, selections ast.SelectionSet, v *Edit) graphql.Marshaler { + return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐEdit(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Mutation_sceneEditUpdate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_Edit(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_sceneEditUpdate_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_performerEditUpdate(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Mutation_performerEditUpdate(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.Mutation().PerformerEditUpdate(ctx, fc.Args["id"].(uuid.UUID), fc.Args["input"].(PerformerEditInput)) + }, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") + if err != nil { + var zeroVal *Edit + return zeroVal, err + } + if ec.Directives.HasRole == nil { + var zeroVal *Edit + return zeroVal, errors.New("directive hasRole is not implemented") + } + return ec.Directives.HasRole(ctx, nil, directive0, role) + } + + next = directive1 + return next + }, + func(ctx context.Context, selections ast.SelectionSet, v *Edit) graphql.Marshaler { + return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐEdit(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Mutation_performerEditUpdate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_Edit(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_performerEditUpdate_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_studioEditUpdate(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Mutation_studioEditUpdate(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.Mutation().StudioEditUpdate(ctx, fc.Args["id"].(uuid.UUID), fc.Args["input"].(StudioEditInput)) + }, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") + if err != nil { + var zeroVal *Edit + return zeroVal, err + } + if ec.Directives.HasRole == nil { + var zeroVal *Edit + return zeroVal, errors.New("directive hasRole is not implemented") + } + return ec.Directives.HasRole(ctx, nil, directive0, role) + } + + next = directive1 + return next + }, + func(ctx context.Context, selections ast.SelectionSet, v *Edit) graphql.Marshaler { + return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐEdit(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Mutation_studioEditUpdate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_Edit(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_studioEditUpdate_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_tagEditUpdate(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Mutation_tagEditUpdate(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.Mutation().TagEditUpdate(ctx, fc.Args["id"].(uuid.UUID), fc.Args["input"].(TagEditInput)) + }, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") + if err != nil { + var zeroVal *Edit + return zeroVal, err + } + if ec.Directives.HasRole == nil { + var zeroVal *Edit + return zeroVal, errors.New("directive hasRole is not implemented") + } + return ec.Directives.HasRole(ctx, nil, directive0, role) + } + + next = directive1 + return next + }, + func(ctx context.Context, selections ast.SelectionSet, v *Edit) graphql.Marshaler { + return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐEdit(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Mutation_tagEditUpdate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, @@ -14866,30 +17409,30 @@ func (ec *executionContext) fieldContext_Mutation_performerEdit(ctx context.Cont } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_performerEdit_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_tagEditUpdate_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_studioEdit(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_editVote(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_studioEdit(ctx, field) + return ec.fieldContext_Mutation_editVote(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().StudioEdit(ctx, fc.Args["input"].(StudioEditInput)) + return ec.Resolvers.Mutation().EditVote(ctx, fc.Args["input"].(EditVoteInput)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next directive1 := func(ctx context.Context) (any, error) { - role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "VOTE") if err != nil { var zeroVal *Edit return zeroVal, err @@ -14911,7 +17454,7 @@ func (ec *executionContext) _Mutation_studioEdit(ctx context.Context, field grap true, ) } -func (ec *executionContext) fieldContext_Mutation_studioEdit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_editVote(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, @@ -14928,24 +17471,24 @@ func (ec *executionContext) fieldContext_Mutation_studioEdit(ctx context.Context } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_studioEdit_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_editVote_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_tagEdit(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_editComment(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_tagEdit(ctx, field) + return ec.fieldContext_Mutation_editComment(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().TagEdit(ctx, fc.Args["input"].(TagEditInput)) + return ec.Resolvers.Mutation().EditComment(ctx, fc.Args["input"].(EditCommentInput)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next @@ -14973,7 +17516,7 @@ func (ec *executionContext) _Mutation_tagEdit(ctx context.Context, field graphql true, ) } -func (ec *executionContext) fieldContext_Mutation_tagEdit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_editComment(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, @@ -14990,36 +17533,36 @@ func (ec *executionContext) fieldContext_Mutation_tagEdit(ctx context.Context, f } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_tagEdit_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_editComment_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_sceneEditUpdate(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_updateEditComment(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_sceneEditUpdate(ctx, field) + return ec.fieldContext_Mutation_updateEditComment(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().SceneEditUpdate(ctx, fc.Args["id"].(uuid.UUID), fc.Args["input"].(SceneEditInput)) + return ec.Resolvers.Mutation().UpdateEditComment(ctx, fc.Args["input"].(UpdateEditCommentInput)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next directive1 := func(ctx context.Context) (any, error) { - role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "MODERATE") if err != nil { - var zeroVal *Edit + var zeroVal *EditComment return zeroVal, err } if ec.Directives.HasRole == nil { - var zeroVal *Edit + var zeroVal *EditComment return zeroVal, errors.New("directive hasRole is not implemented") } return ec.Directives.HasRole(ctx, nil, directive0, role) @@ -15028,21 +17571,21 @@ func (ec *executionContext) _Mutation_sceneEditUpdate(ctx context.Context, field next = directive1 return next }, - func(ctx context.Context, selections ast.SelectionSet, v *Edit) graphql.Marshaler { - return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐEdit(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *EditComment) graphql.Marshaler { + return ec.marshalNEditComment2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐEditComment(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_Mutation_sceneEditUpdate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_updateEditComment(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_Edit(ctx, field) + return ec.childFields_EditComment(ctx, field) }, } defer func() { @@ -15052,36 +17595,36 @@ func (ec *executionContext) fieldContext_Mutation_sceneEditUpdate(ctx context.Co } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_sceneEditUpdate_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_updateEditComment_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_performerEditUpdate(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_hideEditComment(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_performerEditUpdate(ctx, field) + return ec.fieldContext_Mutation_hideEditComment(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().PerformerEditUpdate(ctx, fc.Args["id"].(uuid.UUID), fc.Args["input"].(PerformerEditInput)) + return ec.Resolvers.Mutation().HideEditComment(ctx, fc.Args["input"].(HideEditCommentInput)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next directive1 := func(ctx context.Context) (any, error) { - role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "MODERATE") if err != nil { - var zeroVal *Edit + var zeroVal *EditComment return zeroVal, err } if ec.Directives.HasRole == nil { - var zeroVal *Edit + var zeroVal *EditComment return zeroVal, errors.New("directive hasRole is not implemented") } return ec.Directives.HasRole(ctx, nil, directive0, role) @@ -15090,21 +17633,21 @@ func (ec *executionContext) _Mutation_performerEditUpdate(ctx context.Context, f next = directive1 return next }, - func(ctx context.Context, selections ast.SelectionSet, v *Edit) graphql.Marshaler { - return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐEdit(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *EditComment) graphql.Marshaler { + return ec.marshalNEditComment2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐEditComment(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_Mutation_performerEditUpdate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_hideEditComment(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_Edit(ctx, field) + return ec.childFields_EditComment(ctx, field) }, } defer func() { @@ -15114,30 +17657,30 @@ func (ec *executionContext) fieldContext_Mutation_performerEditUpdate(ctx contex } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_performerEditUpdate_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_hideEditComment_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_studioEditUpdate(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_approveEdit(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_studioEditUpdate(ctx, field) + return ec.fieldContext_Mutation_approveEdit(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().StudioEditUpdate(ctx, fc.Args["id"].(uuid.UUID), fc.Args["input"].(StudioEditInput)) + return ec.Resolvers.Mutation().ApproveEdit(ctx, fc.Args["input"].(ApproveEditInput)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next directive1 := func(ctx context.Context) (any, error) { - role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "MODERATE") if err != nil { var zeroVal *Edit return zeroVal, err @@ -15159,7 +17702,7 @@ func (ec *executionContext) _Mutation_studioEditUpdate(ctx context.Context, fiel true, ) } -func (ec *executionContext) fieldContext_Mutation_studioEditUpdate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_approveEdit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, @@ -15176,24 +17719,24 @@ func (ec *executionContext) fieldContext_Mutation_studioEditUpdate(ctx context.C } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_studioEditUpdate_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_approveEdit_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_tagEditUpdate(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_cancelEdit(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_tagEditUpdate(ctx, field) + return ec.fieldContext_Mutation_cancelEdit(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().TagEditUpdate(ctx, fc.Args["id"].(uuid.UUID), fc.Args["input"].(TagEditInput)) + return ec.Resolvers.Mutation().CancelEdit(ctx, fc.Args["input"].(CancelEditInput)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next @@ -15221,7 +17764,7 @@ func (ec *executionContext) _Mutation_tagEditUpdate(ctx context.Context, field g true, ) } -func (ec *executionContext) fieldContext_Mutation_tagEditUpdate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_cancelEdit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, @@ -15238,36 +17781,36 @@ func (ec *executionContext) fieldContext_Mutation_tagEditUpdate(ctx context.Cont } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_tagEditUpdate_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_cancelEdit_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_editVote(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_deleteEdit(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_editVote(ctx, field) + return ec.fieldContext_Mutation_deleteEdit(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().EditVote(ctx, fc.Args["input"].(EditVoteInput)) + return ec.Resolvers.Mutation().DeleteEdit(ctx, fc.Args["input"].(DeleteEditInput)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next directive1 := func(ctx context.Context) (any, error) { - role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "VOTE") + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "MODERATE") if err != nil { - var zeroVal *Edit + var zeroVal bool return zeroVal, err } if ec.Directives.HasRole == nil { - var zeroVal *Edit + var zeroVal bool return zeroVal, errors.New("directive hasRole is not implemented") } return ec.Directives.HasRole(ctx, nil, directive0, role) @@ -15276,21 +17819,21 @@ func (ec *executionContext) _Mutation_editVote(ctx context.Context, field graphq next = directive1 return next }, - func(ctx context.Context, selections ast.SelectionSet, v *Edit) graphql.Marshaler { - return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐEdit(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_Mutation_editVote(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_deleteEdit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_Edit(ctx, field) + return nil, errors.New("field of type Boolean does not have child fields") }, } defer func() { @@ -15300,30 +17843,30 @@ func (ec *executionContext) fieldContext_Mutation_editVote(ctx context.Context, } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_editVote_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_deleteEdit_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_editComment(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_amendEdit(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_editComment(ctx, field) + return ec.fieldContext_Mutation_amendEdit(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().EditComment(ctx, fc.Args["input"].(EditCommentInput)) + return ec.Resolvers.Mutation().AmendEdit(ctx, fc.Args["input"].(AmendEditInput)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next directive1 := func(ctx context.Context) (any, error) { - role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "MODERATE") if err != nil { var zeroVal *Edit return zeroVal, err @@ -15345,7 +17888,7 @@ func (ec *executionContext) _Mutation_editComment(ctx context.Context, field gra true, ) } -func (ec *executionContext) fieldContext_Mutation_editComment(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_amendEdit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, @@ -15362,36 +17905,36 @@ func (ec *executionContext) fieldContext_Mutation_editComment(ctx context.Contex } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_editComment_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_amendEdit_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_updateEditComment(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_submitFingerprint(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_updateEditComment(ctx, field) + return ec.fieldContext_Mutation_submitFingerprint(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().UpdateEditComment(ctx, fc.Args["input"].(UpdateEditCommentInput)) + return ec.Resolvers.Mutation().SubmitFingerprint(ctx, fc.Args["input"].(FingerprintSubmission)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next directive1 := func(ctx context.Context) (any, error) { - role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "MODERATE") + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "READ") if err != nil { - var zeroVal *EditComment + var zeroVal bool return zeroVal, err } if ec.Directives.HasRole == nil { - var zeroVal *EditComment + var zeroVal bool return zeroVal, errors.New("directive hasRole is not implemented") } return ec.Directives.HasRole(ctx, nil, directive0, role) @@ -15400,21 +17943,21 @@ func (ec *executionContext) _Mutation_updateEditComment(ctx context.Context, fie next = directive1 return next }, - func(ctx context.Context, selections ast.SelectionSet, v *EditComment) graphql.Marshaler { - return ec.marshalNEditComment2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐEditComment(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_Mutation_updateEditComment(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_submitFingerprint(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_EditComment(ctx, field) + return nil, errors.New("field of type Boolean does not have child fields") }, } defer func() { @@ -15424,36 +17967,36 @@ func (ec *executionContext) fieldContext_Mutation_updateEditComment(ctx context. } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_updateEditComment_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_submitFingerprint_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_hideEditComment(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_submitFingerprints(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_hideEditComment(ctx, field) + return ec.fieldContext_Mutation_submitFingerprints(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().HideEditComment(ctx, fc.Args["input"].(HideEditCommentInput)) + return ec.Resolvers.Mutation().SubmitFingerprints(ctx, fc.Args["input"].([]FingerprintBatchSubmission)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next directive1 := func(ctx context.Context) (any, error) { - role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "MODERATE") + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "READ") if err != nil { - var zeroVal *EditComment + var zeroVal []FingerprintSubmissionResult return zeroVal, err } if ec.Directives.HasRole == nil { - var zeroVal *EditComment + var zeroVal []FingerprintSubmissionResult return zeroVal, errors.New("directive hasRole is not implemented") } return ec.Directives.HasRole(ctx, nil, directive0, role) @@ -15462,21 +18005,21 @@ func (ec *executionContext) _Mutation_hideEditComment(ctx context.Context, field next = directive1 return next }, - func(ctx context.Context, selections ast.SelectionSet, v *EditComment) graphql.Marshaler { - return ec.marshalNEditComment2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐEditComment(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v []FingerprintSubmissionResult) graphql.Marshaler { + return ec.marshalNFingerprintSubmissionResult2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐFingerprintSubmissionResultᚄ(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_Mutation_hideEditComment(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_submitFingerprints(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_EditComment(ctx, field) + return ec.childFields_FingerprintSubmissionResult(ctx, field) }, } defer func() { @@ -15486,24 +18029,24 @@ func (ec *executionContext) fieldContext_Mutation_hideEditComment(ctx context.Co } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_hideEditComment_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_submitFingerprints_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_approveEdit(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_sceneMoveFingerprintSubmissions(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_approveEdit(ctx, field) + return ec.fieldContext_Mutation_sceneMoveFingerprintSubmissions(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().ApproveEdit(ctx, fc.Args["input"].(ApproveEditInput)) + return ec.Resolvers.Mutation().SceneMoveFingerprintSubmissions(ctx, fc.Args["input"].(MoveFingerprintSubmissionsInput)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next @@ -15511,11 +18054,11 @@ func (ec *executionContext) _Mutation_approveEdit(ctx context.Context, field gra directive1 := func(ctx context.Context) (any, error) { role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "MODERATE") if err != nil { - var zeroVal *Edit + var zeroVal bool return zeroVal, err } if ec.Directives.HasRole == nil { - var zeroVal *Edit + var zeroVal bool return zeroVal, errors.New("directive hasRole is not implemented") } return ec.Directives.HasRole(ctx, nil, directive0, role) @@ -15524,21 +18067,21 @@ func (ec *executionContext) _Mutation_approveEdit(ctx context.Context, field gra next = directive1 return next }, - func(ctx context.Context, selections ast.SelectionSet, v *Edit) graphql.Marshaler { - return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐEdit(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_Mutation_approveEdit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_sceneMoveFingerprintSubmissions(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_Edit(ctx, field) + return nil, errors.New("field of type Boolean does not have child fields") }, } defer func() { @@ -15548,36 +18091,36 @@ func (ec *executionContext) fieldContext_Mutation_approveEdit(ctx context.Contex } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_approveEdit_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_sceneMoveFingerprintSubmissions_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_cancelEdit(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_sceneDeleteFingerprintSubmissions(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_cancelEdit(ctx, field) + return ec.fieldContext_Mutation_sceneDeleteFingerprintSubmissions(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().CancelEdit(ctx, fc.Args["input"].(CancelEditInput)) + return ec.Resolvers.Mutation().SceneDeleteFingerprintSubmissions(ctx, fc.Args["input"].(DeleteFingerprintSubmissionsInput)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next directive1 := func(ctx context.Context) (any, error) { - role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "MODERATE") if err != nil { - var zeroVal *Edit + var zeroVal bool return zeroVal, err } if ec.Directives.HasRole == nil { - var zeroVal *Edit + var zeroVal bool return zeroVal, errors.New("directive hasRole is not implemented") } return ec.Directives.HasRole(ctx, nil, directive0, role) @@ -15586,21 +18129,21 @@ func (ec *executionContext) _Mutation_cancelEdit(ctx context.Context, field grap next = directive1 return next }, - func(ctx context.Context, selections ast.SelectionSet, v *Edit) graphql.Marshaler { - return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐEdit(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_Mutation_cancelEdit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_sceneDeleteFingerprintSubmissions(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_Edit(ctx, field) + return nil, errors.New("field of type Boolean does not have child fields") }, } defer func() { @@ -15610,36 +18153,36 @@ func (ec *executionContext) fieldContext_Mutation_cancelEdit(ctx context.Context } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_cancelEdit_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_sceneDeleteFingerprintSubmissions_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_deleteEdit(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_submitSceneDraft(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_deleteEdit(ctx, field) + return ec.fieldContext_Mutation_submitSceneDraft(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().DeleteEdit(ctx, fc.Args["input"].(DeleteEditInput)) + return ec.Resolvers.Mutation().SubmitSceneDraft(ctx, fc.Args["input"].(SceneDraftInput)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next directive1 := func(ctx context.Context) (any, error) { - role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "MODERATE") + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") if err != nil { - var zeroVal bool + var zeroVal *DraftSubmissionStatus return zeroVal, err } if ec.Directives.HasRole == nil { - var zeroVal bool + var zeroVal *DraftSubmissionStatus return zeroVal, errors.New("directive hasRole is not implemented") } return ec.Directives.HasRole(ctx, nil, directive0, role) @@ -15648,21 +18191,21 @@ func (ec *executionContext) _Mutation_deleteEdit(ctx context.Context, field grap next = directive1 return next }, - func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { - return ec.marshalNBoolean2bool(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *DraftSubmissionStatus) graphql.Marshaler { + return ec.marshalNDraftSubmissionStatus2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐDraftSubmissionStatus(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_Mutation_deleteEdit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_submitSceneDraft(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Boolean does not have child fields") + return ec.childFields_DraftSubmissionStatus(ctx, field) }, } defer func() { @@ -15672,36 +18215,36 @@ func (ec *executionContext) fieldContext_Mutation_deleteEdit(ctx context.Context } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_deleteEdit_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_submitSceneDraft_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_amendEdit(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_submitPerformerDraft(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_amendEdit(ctx, field) + return ec.fieldContext_Mutation_submitPerformerDraft(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().AmendEdit(ctx, fc.Args["input"].(AmendEditInput)) + return ec.Resolvers.Mutation().SubmitPerformerDraft(ctx, fc.Args["input"].(PerformerDraftInput)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next directive1 := func(ctx context.Context) (any, error) { - role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "MODERATE") + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") if err != nil { - var zeroVal *Edit + var zeroVal *DraftSubmissionStatus return zeroVal, err } if ec.Directives.HasRole == nil { - var zeroVal *Edit + var zeroVal *DraftSubmissionStatus return zeroVal, errors.New("directive hasRole is not implemented") } return ec.Directives.HasRole(ctx, nil, directive0, role) @@ -15710,21 +18253,21 @@ func (ec *executionContext) _Mutation_amendEdit(ctx context.Context, field graph next = directive1 return next }, - func(ctx context.Context, selections ast.SelectionSet, v *Edit) graphql.Marshaler { - return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐEdit(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *DraftSubmissionStatus) graphql.Marshaler { + return ec.marshalNDraftSubmissionStatus2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐDraftSubmissionStatus(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_Mutation_amendEdit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_submitPerformerDraft(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_Edit(ctx, field) + return ec.childFields_DraftSubmissionStatus(ctx, field) }, } defer func() { @@ -15734,30 +18277,30 @@ func (ec *executionContext) fieldContext_Mutation_amendEdit(ctx context.Context, } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_amendEdit_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_submitPerformerDraft_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_submitFingerprint(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_destroyDraft(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_submitFingerprint(ctx, field) + return ec.fieldContext_Mutation_destroyDraft(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().SubmitFingerprint(ctx, fc.Args["input"].(FingerprintSubmission)) + return ec.Resolvers.Mutation().DestroyDraft(ctx, fc.Args["id"].(uuid.UUID)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next directive1 := func(ctx context.Context) (any, error) { - role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "READ") + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") if err != nil { var zeroVal bool return zeroVal, err @@ -15779,7 +18322,7 @@ func (ec *executionContext) _Mutation_submitFingerprint(ctx context.Context, fie true, ) } -func (ec *executionContext) fieldContext_Mutation_submitFingerprint(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_destroyDraft(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, @@ -15796,340 +18339,30 @@ func (ec *executionContext) fieldContext_Mutation_submitFingerprint(ctx context. } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_submitFingerprint_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_destroyDraft_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_submitFingerprints(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_favoritePerformer(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_submitFingerprints(ctx, field) + return ec.fieldContext_Mutation_favoritePerformer(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().SubmitFingerprints(ctx, fc.Args["input"].([]FingerprintBatchSubmission)) + return ec.Resolvers.Mutation().FavoritePerformer(ctx, fc.Args["id"].(uuid.UUID), fc.Args["favorite"].(bool)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next directive1 := func(ctx context.Context) (any, error) { role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "READ") - if err != nil { - var zeroVal []FingerprintSubmissionResult - return zeroVal, err - } - if ec.Directives.HasRole == nil { - var zeroVal []FingerprintSubmissionResult - return zeroVal, errors.New("directive hasRole is not implemented") - } - return ec.Directives.HasRole(ctx, nil, directive0, role) - } - - next = directive1 - return next - }, - func(ctx context.Context, selections ast.SelectionSet, v []FingerprintSubmissionResult) graphql.Marshaler { - return ec.marshalNFingerprintSubmissionResult2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐFingerprintSubmissionResultᚄ(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_Mutation_submitFingerprints(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Mutation", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_FingerprintSubmissionResult(ctx, field) - }, - } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_submitFingerprints_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } - return fc, nil -} - -func (ec *executionContext) _Mutation_sceneMoveFingerprintSubmissions(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_sceneMoveFingerprintSubmissions(ctx, field) - }, - func(ctx context.Context) (any, error) { - fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().SceneMoveFingerprintSubmissions(ctx, fc.Args["input"].(MoveFingerprintSubmissionsInput)) - }, - func(ctx context.Context, next graphql.Resolver) graphql.Resolver { - directive0 := next - - directive1 := func(ctx context.Context) (any, error) { - role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "MODERATE") - if err != nil { - var zeroVal bool - return zeroVal, err - } - if ec.Directives.HasRole == nil { - var zeroVal bool - return zeroVal, errors.New("directive hasRole is not implemented") - } - return ec.Directives.HasRole(ctx, nil, directive0, role) - } - - next = directive1 - return next - }, - func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { - return ec.marshalNBoolean2bool(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_Mutation_sceneMoveFingerprintSubmissions(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Mutation", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Boolean does not have child fields") - }, - } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_sceneMoveFingerprintSubmissions_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } - return fc, nil -} - -func (ec *executionContext) _Mutation_sceneDeleteFingerprintSubmissions(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_sceneDeleteFingerprintSubmissions(ctx, field) - }, - func(ctx context.Context) (any, error) { - fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().SceneDeleteFingerprintSubmissions(ctx, fc.Args["input"].(DeleteFingerprintSubmissionsInput)) - }, - func(ctx context.Context, next graphql.Resolver) graphql.Resolver { - directive0 := next - - directive1 := func(ctx context.Context) (any, error) { - role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "MODERATE") - if err != nil { - var zeroVal bool - return zeroVal, err - } - if ec.Directives.HasRole == nil { - var zeroVal bool - return zeroVal, errors.New("directive hasRole is not implemented") - } - return ec.Directives.HasRole(ctx, nil, directive0, role) - } - - next = directive1 - return next - }, - func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { - return ec.marshalNBoolean2bool(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_Mutation_sceneDeleteFingerprintSubmissions(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Mutation", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Boolean does not have child fields") - }, - } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_sceneDeleteFingerprintSubmissions_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } - return fc, nil -} - -func (ec *executionContext) _Mutation_submitSceneDraft(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_submitSceneDraft(ctx, field) - }, - func(ctx context.Context) (any, error) { - fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().SubmitSceneDraft(ctx, fc.Args["input"].(SceneDraftInput)) - }, - func(ctx context.Context, next graphql.Resolver) graphql.Resolver { - directive0 := next - - directive1 := func(ctx context.Context) (any, error) { - role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") - if err != nil { - var zeroVal *DraftSubmissionStatus - return zeroVal, err - } - if ec.Directives.HasRole == nil { - var zeroVal *DraftSubmissionStatus - return zeroVal, errors.New("directive hasRole is not implemented") - } - return ec.Directives.HasRole(ctx, nil, directive0, role) - } - - next = directive1 - return next - }, - func(ctx context.Context, selections ast.SelectionSet, v *DraftSubmissionStatus) graphql.Marshaler { - return ec.marshalNDraftSubmissionStatus2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐDraftSubmissionStatus(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_Mutation_submitSceneDraft(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Mutation", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_DraftSubmissionStatus(ctx, field) - }, - } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_submitSceneDraft_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } - return fc, nil -} - -func (ec *executionContext) _Mutation_submitPerformerDraft(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_submitPerformerDraft(ctx, field) - }, - func(ctx context.Context) (any, error) { - fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().SubmitPerformerDraft(ctx, fc.Args["input"].(PerformerDraftInput)) - }, - func(ctx context.Context, next graphql.Resolver) graphql.Resolver { - directive0 := next - - directive1 := func(ctx context.Context) (any, error) { - role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") - if err != nil { - var zeroVal *DraftSubmissionStatus - return zeroVal, err - } - if ec.Directives.HasRole == nil { - var zeroVal *DraftSubmissionStatus - return zeroVal, errors.New("directive hasRole is not implemented") - } - return ec.Directives.HasRole(ctx, nil, directive0, role) - } - - next = directive1 - return next - }, - func(ctx context.Context, selections ast.SelectionSet, v *DraftSubmissionStatus) graphql.Marshaler { - return ec.marshalNDraftSubmissionStatus2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐDraftSubmissionStatus(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_Mutation_submitPerformerDraft(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Mutation", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_DraftSubmissionStatus(ctx, field) - }, - } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_submitPerformerDraft_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } - return fc, nil -} - -func (ec *executionContext) _Mutation_destroyDraft(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_destroyDraft(ctx, field) - }, - func(ctx context.Context) (any, error) { - fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().DestroyDraft(ctx, fc.Args["id"].(uuid.UUID)) - }, - func(ctx context.Context, next graphql.Resolver) graphql.Resolver { - directive0 := next - - directive1 := func(ctx context.Context) (any, error) { - role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "EDIT") if err != nil { var zeroVal bool return zeroVal, err @@ -16151,7 +18384,7 @@ func (ec *executionContext) _Mutation_destroyDraft(ctx context.Context, field gr true, ) } -func (ec *executionContext) fieldContext_Mutation_destroyDraft(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_favoritePerformer(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, @@ -16168,24 +18401,24 @@ func (ec *executionContext) fieldContext_Mutation_destroyDraft(ctx context.Conte } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_destroyDraft_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_favoritePerformer_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_favoritePerformer(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_favoriteStudio(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_favoritePerformer(ctx, field) + return ec.fieldContext_Mutation_favoriteStudio(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().FavoritePerformer(ctx, fc.Args["id"].(uuid.UUID), fc.Args["favorite"].(bool)) + return ec.Resolvers.Mutation().FavoriteStudio(ctx, fc.Args["id"].(uuid.UUID), fc.Args["favorite"].(bool)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next @@ -16213,7 +18446,7 @@ func (ec *executionContext) _Mutation_favoritePerformer(ctx context.Context, fie true, ) } -func (ec *executionContext) fieldContext_Mutation_favoritePerformer(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_favoriteStudio(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, @@ -16230,24 +18463,24 @@ func (ec *executionContext) fieldContext_Mutation_favoritePerformer(ctx context. } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_favoritePerformer_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_favoriteStudio_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_favoriteStudio(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_markNotificationsRead(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_favoriteStudio(ctx, field) + return ec.fieldContext_Mutation_markNotificationsRead(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().FavoriteStudio(ctx, fc.Args["id"].(uuid.UUID), fc.Args["favorite"].(bool)) + return ec.Resolvers.Mutation().MarkNotificationsRead(ctx, fc.Args["notification"].(*MarkNotificationReadInput)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next @@ -16275,7 +18508,7 @@ func (ec *executionContext) _Mutation_favoriteStudio(ctx context.Context, field true, ) } -func (ec *executionContext) fieldContext_Mutation_favoriteStudio(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_markNotificationsRead(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, @@ -16292,24 +18525,24 @@ func (ec *executionContext) fieldContext_Mutation_favoriteStudio(ctx context.Con } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_favoriteStudio_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_markNotificationsRead_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_markNotificationsRead(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_updateNotificationSubscriptions(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_markNotificationsRead(ctx, field) + return ec.fieldContext_Mutation_updateNotificationSubscriptions(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().MarkNotificationsRead(ctx, fc.Args["notification"].(*MarkNotificationReadInput)) + return ec.Resolvers.Mutation().UpdateNotificationSubscriptions(ctx, fc.Args["subscriptions"].([]NotificationEnum)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next @@ -16337,7 +18570,7 @@ func (ec *executionContext) _Mutation_markNotificationsRead(ctx context.Context, true, ) } -func (ec *executionContext) fieldContext_Mutation_markNotificationsRead(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_updateNotificationSubscriptions(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, @@ -16354,24 +18587,24 @@ func (ec *executionContext) fieldContext_Mutation_markNotificationsRead(ctx cont } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_markNotificationsRead_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_updateNotificationSubscriptions_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Mutation_updateNotificationSubscriptions(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_updateImageTypePreferences(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_updateNotificationSubscriptions(ctx, field) + return ec.fieldContext_Mutation_updateImageTypePreferences(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().UpdateNotificationSubscriptions(ctx, fc.Args["subscriptions"].([]NotificationEnum)) + return ec.Resolvers.Mutation().UpdateImageTypePreferences(ctx, fc.Args["input"].(ImageTypePreferencesInput)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next @@ -16399,7 +18632,7 @@ func (ec *executionContext) _Mutation_updateNotificationSubscriptions(ctx contex true, ) } -func (ec *executionContext) fieldContext_Mutation_updateNotificationSubscriptions(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_updateImageTypePreferences(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, @@ -16416,7 +18649,7 @@ func (ec *executionContext) fieldContext_Mutation_updateNotificationSubscription } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_updateNotificationSubscriptions_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_updateImageTypePreferences_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } @@ -17167,6 +19400,70 @@ func (ec *executionContext) fieldContext_Performer_images(_ context.Context, fie return fc, nil } +func (ec *executionContext) _Performer_typed_images(ctx context.Context, field graphql.CollectedField, obj *Performer) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Performer_typed_images(ctx, field) + }, + func(ctx context.Context) (any, error) { + return ec.Resolvers.Performer().TypedImages(ctx, obj) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []TypedImage) graphql.Marshaler { + return ec.marshalNTypedImage2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐTypedImageᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Performer_typed_images(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Performer", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_TypedImage(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _Performer_thumbnail(ctx context.Context, field graphql.CollectedField, obj *Performer) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Performer_thumbnail(ctx, field) + }, + func(ctx context.Context) (any, error) { + return ec.Resolvers.Performer().Thumbnail(ctx, obj) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *Image) graphql.Marshaler { + return ec.marshalOImage2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImage(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_Performer_thumbnail(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Performer", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_Image(ctx, field) + }, + } + return fc, nil +} + func (ec *executionContext) _Performer_deleted(ctx context.Context, field graphql.CollectedField, obj *Performer) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -18709,6 +21006,70 @@ func (ec *executionContext) fieldContext_PerformerEdit_removed_images(_ context. return fc, nil } +func (ec *executionContext) _PerformerEdit_image_changes(ctx context.Context, field graphql.CollectedField, obj *PerformerEdit) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PerformerEdit_image_changes(ctx, field) + }, + func(ctx context.Context) (any, error) { + return ec.Resolvers.PerformerEdit().ImageChanges(ctx, obj) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []ImageAssignmentChange) graphql.Marshaler { + return ec.marshalNImageAssignmentChange2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageAssignmentChangeᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PerformerEdit_image_changes(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PerformerEdit", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_ImageAssignmentChange(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _PerformerEdit_typed_images(ctx context.Context, field graphql.CollectedField, obj *PerformerEdit) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PerformerEdit_typed_images(ctx, field) + }, + func(ctx context.Context) (any, error) { + return ec.Resolvers.PerformerEdit().TypedImages(ctx, obj) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []TypedImage) graphql.Marshaler { + return ec.marshalNTypedImage2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐTypedImageᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PerformerEdit_typed_images(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PerformerEdit", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_TypedImage(ctx, field) + }, + } + return fc, nil +} + func (ec *executionContext) _PerformerEdit_draft_id(ctx context.Context, field graphql.CollectedField, obj *PerformerEdit) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -20282,6 +22643,68 @@ func (ec *executionContext) fieldContext_Query_fetchSiteFavicons(ctx context.Con return fc, nil } +func (ec *executionContext) _Query_imageTypeGroups(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Query_imageTypeGroups(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.Query().ImageTypeGroups(ctx, fc.Args["target"].(*ImageTypeScopeEnum), fc.Args["include_disabled"].(*bool)) + }, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐRoleEnum(ctx, "READ") + if err != nil { + var zeroVal []ImageTypeGroup + return zeroVal, err + } + if ec.Directives.HasRole == nil { + var zeroVal []ImageTypeGroup + return zeroVal, errors.New("directive hasRole is not implemented") + } + return ec.Directives.HasRole(ctx, nil, directive0, role) + } + + next = directive1 + return next + }, + func(ctx context.Context, selections ast.SelectionSet, v []ImageTypeGroup) graphql.Marshaler { + return ec.marshalNImageTypeGroup2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Query_imageTypeGroups(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_ImageTypeGroup(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_imageTypeGroups_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Query_findEdit(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -25664,6 +28087,84 @@ func (ec *executionContext) fieldContext_TagEdit_aliases(_ context.Context, fiel return graphql.NewScalarFieldContext("TagEdit", field, true, true, errors.New("field of type String does not have child fields")) } +func (ec *executionContext) _TypedImage_image(ctx context.Context, field graphql.CollectedField, obj *TypedImage) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_TypedImage_image(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Image, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *Image) graphql.Marshaler { + return ec.marshalNImage2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImage(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_TypedImage_image(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TypedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_Image(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _TypedImage_types(ctx context.Context, field graphql.CollectedField, obj *TypedImage) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_TypedImage_types(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Types, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []ImageTypeEnum) graphql.Marshaler { + return ec.marshalNImageTypeEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnumᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_TypedImage_types(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("TypedImage", field, false, false, errors.New("field of type ImageTypeEnum does not have child fields")) +} + +func (ec *executionContext) _TypedImage_date(ctx context.Context, field graphql.CollectedField, obj *TypedImage) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_TypedImage_date(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Date, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_TypedImage_date(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("TypedImage", field, false, false, errors.New("field of type String does not have child fields")) +} + func (ec *executionContext) _URL_url(ctx context.Context, field graphql.CollectedField, obj *URL) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -26010,6 +28511,78 @@ func (ec *executionContext) fieldContext_User_notification_subscriptions(_ conte return graphql.NewScalarFieldContext("User", field, true, true, errors.New("field of type NotificationEnum does not have child fields")) } +func (ec *executionContext) _User_image_type_preferences(ctx context.Context, field graphql.CollectedField, obj *User) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_User_image_type_preferences(ctx, field) + }, + func(ctx context.Context) (any, error) { + return ec.Resolvers.User().ImageTypePreferences(ctx, obj) + }, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + if ec.Directives.IsUserOwner == nil { + var zeroVal []ImageTypeEnum + return zeroVal, errors.New("directive isUserOwner is not implemented") + } + return ec.Directives.IsUserOwner(ctx, obj, directive0) + } + + next = directive1 + return next + }, + func(ctx context.Context, selections ast.SelectionSet, v []ImageTypeEnum) graphql.Marshaler { + return ec.marshalNImageTypeEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnumᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_User_image_type_preferences(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("User", field, true, true, errors.New("field of type ImageTypeEnum does not have child fields")) +} + +func (ec *executionContext) _User_image_type_group_preferences(ctx context.Context, field graphql.CollectedField, obj *User) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_User_image_type_group_preferences(ctx, field) + }, + func(ctx context.Context) (any, error) { + return ec.Resolvers.User().ImageTypeGroupPreferences(ctx, obj) + }, + func(ctx context.Context, next graphql.Resolver) graphql.Resolver { + directive0 := next + + directive1 := func(ctx context.Context) (any, error) { + if ec.Directives.IsUserOwner == nil { + var zeroVal []ImageTypeGroupEnum + return zeroVal, errors.New("directive isUserOwner is not implemented") + } + return ec.Directives.IsUserOwner(ctx, obj, directive0) + } + + next = directive1 + return next + }, + func(ctx context.Context, selections ast.SelectionSet, v []ImageTypeGroupEnum) graphql.Marshaler { + return ec.marshalNImageTypeGroupEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupEnumᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_User_image_type_group_preferences(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("User", field, true, true, errors.New("field of type ImageTypeGroupEnum does not have child fields")) +} + func (ec *executionContext) _User_vote_count(ctx context.Context, field graphql.CollectedField, obj *User) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -29137,6 +31710,50 @@ func (ec *executionContext) unmarshalInputIDCriterionInput(ctx context.Context, return it, nil } +func (ec *executionContext) unmarshalInputImageAssignmentInput(ctx context.Context, obj any) (ImageAssignmentInput, error) { + var it ImageAssignmentInput + if obj == nil { + return it, nil + } + + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"image_id", "types", "date"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "image_id": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("image_id")) + data, err := ec.unmarshalNID2githubᚗcomᚋgofrsᚋuuidᚐUUID(ctx, v) + if err != nil { + return it, err + } + it.ImageID = data + case "types": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("types")) + data, err := ec.unmarshalNImageTypeEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnumᚄ(ctx, v) + if err != nil { + return it, err + } + it.Types = data + case "date": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("date")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.Date = data + } + } + return it, nil +} + func (ec *executionContext) unmarshalInputImageCreateInput(ctx context.Context, obj any) (ImageCreateInput, error) { var it ImageCreateInput if obj == nil { @@ -29148,7 +31765,7 @@ func (ec *executionContext) unmarshalInputImageCreateInput(ctx context.Context, asMap[k] = v } - fieldsInOrder := [...]string{"url", "file"} + fieldsInOrder := [...]string{"url", "file", "crop"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -29169,6 +31786,75 @@ func (ec *executionContext) unmarshalInputImageCreateInput(ctx context.Context, return it, err } it.File = data + case "crop": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("crop")) + data, err := ec.unmarshalOImageCropInput2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageCropInput(ctx, v) + if err != nil { + return it, err + } + it.Crop = data + } + } + return it, nil +} + +func (ec *executionContext) unmarshalInputImageCropInput(ctx context.Context, obj any) (ImageCropInput, error) { + var it ImageCropInput + if obj == nil { + return it, nil + } + + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + if _, present := asMap["angle"]; !present { + asMap["angle"] = 0 + } + + fieldsInOrder := [...]string{"x", "y", "width", "height", "angle"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "x": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("x")) + data, err := ec.unmarshalNFloat2float64(ctx, v) + if err != nil { + return it, err + } + it.X = data + case "y": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("y")) + data, err := ec.unmarshalNFloat2float64(ctx, v) + if err != nil { + return it, err + } + it.Y = data + case "width": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("width")) + data, err := ec.unmarshalNFloat2float64(ctx, v) + if err != nil { + return it, err + } + it.Width = data + case "height": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("height")) + data, err := ec.unmarshalNFloat2float64(ctx, v) + if err != nil { + return it, err + } + it.Height = data + case "angle": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("angle")) + data, err := ec.unmarshalOFloat2ᚖfloat64(ctx, v) + if err != nil { + return it, err + } + it.Angle = data } } return it, nil @@ -29204,6 +31890,124 @@ func (ec *executionContext) unmarshalInputImageDestroyInput(ctx context.Context, return it, nil } +func (ec *executionContext) unmarshalInputImageTypeEnabledInput(ctx context.Context, obj any) (ImageTypeEnabledInput, error) { + var it ImageTypeEnabledInput + if obj == nil { + return it, nil + } + + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + if _, present := asMap["disabled_groups"]; !present { + asMap["disabled_groups"] = []any{} + } + if _, present := asMap["disabled_types"]; !present { + asMap["disabled_types"] = []any{} + } + + fieldsInOrder := [...]string{"disabled_groups", "disabled_types"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "disabled_groups": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("disabled_groups")) + data, err := ec.unmarshalNImageTypeGroupEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupEnumᚄ(ctx, v) + if err != nil { + return it, err + } + it.DisabledGroups = data + case "disabled_types": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("disabled_types")) + data, err := ec.unmarshalNImageTypeEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnumᚄ(ctx, v) + if err != nil { + return it, err + } + it.DisabledTypes = data + } + } + return it, nil +} + +func (ec *executionContext) unmarshalInputImageTypeOrderInput(ctx context.Context, obj any) (ImageTypeOrderInput, error) { + var it ImageTypeOrderInput + if obj == nil { + return it, nil + } + + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"groups", "types"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "groups": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("groups")) + data, err := ec.unmarshalNImageTypeGroupEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupEnumᚄ(ctx, v) + if err != nil { + return it, err + } + it.Groups = data + case "types": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("types")) + data, err := ec.unmarshalNImageTypeEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnumᚄ(ctx, v) + if err != nil { + return it, err + } + it.Types = data + } + } + return it, nil +} + +func (ec *executionContext) unmarshalInputImageTypePreferencesInput(ctx context.Context, obj any) (ImageTypePreferencesInput, error) { + var it ImageTypePreferencesInput + if obj == nil { + return it, nil + } + + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"types", "groups"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "types": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("types")) + data, err := ec.unmarshalNImageTypeEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnumᚄ(ctx, v) + if err != nil { + return it, err + } + it.Types = data + case "groups": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("groups")) + data, err := ec.unmarshalOImageTypeGroupEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupEnumᚄ(ctx, v) + if err != nil { + return it, err + } + it.Groups = data + } + } + return it, nil +} + func (ec *executionContext) unmarshalInputImageUpdateInput(ctx context.Context, obj any) (ImageUpdateInput, error) { var it ImageUpdateInput if obj == nil { @@ -29576,7 +32380,7 @@ func (ec *executionContext) unmarshalInputPerformerCreateInput(ctx context.Conte asMap[k] = v } - fieldsInOrder := [...]string{"name", "disambiguation", "aliases", "gender", "urls", "birthdate", "deathdate", "ethnicity", "country", "eye_color", "hair_color", "height", "cup_size", "band_size", "waist_size", "hip_size", "breast_type", "career_start_year", "career_end_year", "tattoos", "piercings", "image_ids", "draft_id"} + fieldsInOrder := [...]string{"name", "disambiguation", "aliases", "gender", "urls", "birthdate", "deathdate", "ethnicity", "country", "eye_color", "hair_color", "height", "cup_size", "band_size", "waist_size", "hip_size", "breast_type", "career_start_year", "career_end_year", "tattoos", "piercings", "image_ids", "image_types", "draft_id"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -29737,6 +32541,13 @@ func (ec *executionContext) unmarshalInputPerformerCreateInput(ctx context.Conte return it, err } it.ImageIds = data + case "image_types": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("image_types")) + data, err := ec.unmarshalOImageAssignmentInput2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageAssignmentInputᚄ(ctx, v) + if err != nil { + return it, err + } + it.ImageTypes = data case "draft_id": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("draft_id")) data, err := ec.unmarshalOID2ᚖgithubᚗcomᚋgofrsᚋuuidᚐUUID(ctx, v) @@ -29953,7 +32764,7 @@ func (ec *executionContext) unmarshalInputPerformerEditDetailsInput(ctx context. asMap[k] = v } - fieldsInOrder := [...]string{"name", "disambiguation", "aliases", "gender", "urls", "birthdate", "deathdate", "ethnicity", "country", "eye_color", "hair_color", "height", "cup_size", "band_size", "waist_size", "hip_size", "breast_type", "career_start_year", "career_end_year", "tattoos", "piercings", "image_ids", "draft_id"} + fieldsInOrder := [...]string{"name", "disambiguation", "aliases", "gender", "urls", "birthdate", "deathdate", "ethnicity", "country", "eye_color", "hair_color", "height", "cup_size", "band_size", "waist_size", "hip_size", "breast_type", "career_start_year", "career_end_year", "tattoos", "piercings", "image_ids", "image_types", "draft_id"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -30114,6 +32925,13 @@ func (ec *executionContext) unmarshalInputPerformerEditDetailsInput(ctx context. return it, err } it.ImageIds = data + case "image_types": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("image_types")) + data, err := ec.unmarshalOImageAssignmentInput2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageAssignmentInputᚄ(ctx, v) + if err != nil { + return it, err + } + it.ImageTypes = data case "draft_id": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("draft_id")) data, err := ec.unmarshalOID2ᚖgithubᚗcomᚋgofrsᚋuuidᚐUUID(ctx, v) @@ -30552,7 +33370,7 @@ func (ec *executionContext) unmarshalInputPerformerUpdateInput(ctx context.Conte asMap[k] = v } - fieldsInOrder := [...]string{"id", "name", "disambiguation", "aliases", "gender", "urls", "birthdate", "deathdate", "ethnicity", "country", "eye_color", "hair_color", "height", "cup_size", "band_size", "waist_size", "hip_size", "breast_type", "career_start_year", "career_end_year", "tattoos", "piercings", "image_ids"} + fieldsInOrder := [...]string{"id", "name", "disambiguation", "aliases", "gender", "urls", "birthdate", "deathdate", "ethnicity", "country", "eye_color", "hair_color", "height", "cup_size", "band_size", "waist_size", "hip_size", "breast_type", "career_start_year", "career_end_year", "tattoos", "piercings", "image_ids", "image_types"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -30720,6 +33538,13 @@ func (ec *executionContext) unmarshalInputPerformerUpdateInput(ctx context.Conte return it, err } it.ImageIds = data + case "image_types": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("image_types")) + data, err := ec.unmarshalOImageAssignmentInput2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageAssignmentInputᚄ(ctx, v) + if err != nil { + return it, err + } + it.ImageTypes = data } } return it, nil @@ -33896,6 +36721,286 @@ func (ec *executionContext) _CommentVotedEdit(ctx context.Context, sel ast.Selec return out } +var cropGuideImplementors = []string{"CropGuide"} + +func (ec *executionContext) _CropGuide(ctx context.Context, sel ast.SelectionSet, obj *CropGuide) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, cropGuideImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("CropGuide") + case "axis": + out.Values[i] = ec._CropGuide_axis(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "position": + out.Values[i] = ec._CropGuide_position(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "role": + out.Values[i] = ec._CropGuide_role(ctx, field, obj) + case "label": + out.Values[i] = ec._CropGuide_label(ctx, field, obj) + case "pivot": + out.Values[i] = ec._CropGuide_pivot(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var cropKnotImplementors = []string{"CropKnot"} + +func (ec *executionContext) _CropKnot(ctx context.Context, sel ast.SelectionSet, obj *CropKnot) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, cropKnotImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("CropKnot") + case "control_in": + out.Values[i] = ec._CropKnot_control_in(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "anchor": + out.Values[i] = ec._CropKnot_anchor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "control_out": + out.Values[i] = ec._CropKnot_control_out(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var cropPointImplementors = []string{"CropPoint"} + +func (ec *executionContext) _CropPoint(ctx context.Context, sel ast.SelectionSet, obj *CropPoint) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, cropPointImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("CropPoint") + case "x": + out.Values[i] = ec._CropPoint_x(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "y": + out.Values[i] = ec._CropPoint_y(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var cropShapeImplementors = []string{"CropShape"} + +func (ec *executionContext) _CropShape(ctx context.Context, sel ast.SelectionSet, obj *CropShape) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, cropShapeImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("CropShape") + case "label": + out.Values[i] = ec._CropShape_label(ctx, field, obj) + case "subpaths": + out.Values[i] = ec._CropShape_subpaths(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var cropSubpathImplementors = []string{"CropSubpath"} + +func (ec *executionContext) _CropSubpath(ctx context.Context, sel ast.SelectionSet, obj *CropSubpath) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, cropSubpathImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("CropSubpath") + case "closed": + out.Values[i] = ec._CropSubpath_closed(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "knots": + out.Values[i] = ec._CropSubpath_knots(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var cropTemplateImplementors = []string{"CropTemplate"} + +func (ec *executionContext) _CropTemplate(ctx context.Context, sel ast.SelectionSet, obj *CropTemplate) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, cropTemplateImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("CropTemplate") + case "aspect_ratio": + out.Values[i] = ec._CropTemplate_aspect_ratio(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "guides": + out.Values[i] = ec._CropTemplate_guides(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "shapes": + out.Values[i] = ec._CropTemplate_shapes(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var downvoteOwnEditImplementors = []string{"DownvoteOwnEdit", "NotificationData"} func (ec *executionContext) _DownvoteOwnEdit(ctx context.Context, sel ast.SelectionSet, obj *DownvoteOwnEdit) graphql.Marshaler { @@ -35811,155 +38916,376 @@ func (ec *executionContext) _FingerprintedSceneEdit(ctx context.Context, sel ast return out } -var fuzzyDateImplementors = []string{"FuzzyDate"} +var fuzzyDateImplementors = []string{"FuzzyDate"} + +func (ec *executionContext) _FuzzyDate(ctx context.Context, sel ast.SelectionSet, obj *FuzzyDate) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, fuzzyDateImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("FuzzyDate") + case "date": + out.Values[i] = ec._FuzzyDate_date(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "accuracy": + out.Values[i] = ec._FuzzyDate_accuracy(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var genderFacetImplementors = []string{"GenderFacet"} + +func (ec *executionContext) _GenderFacet(ctx context.Context, sel ast.SelectionSet, obj *GenderFacet) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, genderFacetImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GenderFacet") + case "gender": + out.Values[i] = ec._GenderFacet_gender(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "count": + out.Values[i] = ec._GenderFacet_count(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var imageImplementors = []string{"Image"} + +func (ec *executionContext) _Image(ctx context.Context, sel ast.SelectionSet, obj *Image) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, imageImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Image") + case "id": + out.Values[i] = ec._Image_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "url": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Image_url(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "width": + out.Values[i] = ec._Image_width(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "height": + out.Values[i] = ec._Image_height(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var imageAssignmentChangeImplementors = []string{"ImageAssignmentChange"} + +func (ec *executionContext) _ImageAssignmentChange(ctx context.Context, sel ast.SelectionSet, obj *ImageAssignmentChange) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, imageAssignmentChangeImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ImageAssignmentChange") + case "image": + out.Values[i] = ec._ImageAssignmentChange_image(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "added_types": + out.Values[i] = ec._ImageAssignmentChange_added_types(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "removed_types": + out.Values[i] = ec._ImageAssignmentChange_removed_types(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "date": + out.Values[i] = ec._ImageAssignmentChange_date(ctx, field, obj) + case "date_changed": + out.Values[i] = ec._ImageAssignmentChange_date_changed(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var imageTypeImplementors = []string{"ImageType"} + +func (ec *executionContext) _ImageType(ctx context.Context, sel ast.SelectionSet, obj *ImageType) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, imageTypeImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ImageType") + case "key": + out.Values[i] = ec._ImageType_key(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "name": + out.Values[i] = ec._ImageType_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "description": + out.Values[i] = ec._ImageType_description(ctx, field, obj) + case "sort_order": + out.Values[i] = ec._ImageType_sort_order(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "valid_types": + out.Values[i] = ec._ImageType_valid_types(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "enabled": + out.Values[i] = ec._ImageType_enabled(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "conflicts_with": + out.Values[i] = ec._ImageType_conflicts_with(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "crop_template": + field := field + + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._ImageType_crop_template(ctx, field, obj) + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var imageTypeGroupImplementors = []string{"ImageTypeGroup"} -func (ec *executionContext) _FuzzyDate(ctx context.Context, sel ast.SelectionSet, obj *FuzzyDate) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, fuzzyDateImplementors) +func (ec *executionContext) _ImageTypeGroup(ctx context.Context, sel ast.SelectionSet, obj *ImageTypeGroup) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, imageTypeGroupImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("FuzzyDate") - case "date": - out.Values[i] = ec._FuzzyDate_date(ctx, field, obj) + out.Values[i] = graphql.MarshalString("ImageTypeGroup") + case "key": + out.Values[i] = ec._ImageTypeGroup_key(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "accuracy": - out.Values[i] = ec._FuzzyDate_accuracy(ctx, field, obj) + case "name": + out.Values[i] = ec._ImageTypeGroup_name(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) - - for label, dfs := range deferred { - ec.ProcessDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - -var genderFacetImplementors = []string{"GenderFacet"} - -func (ec *executionContext) _GenderFacet(ctx context.Context, sel ast.SelectionSet, obj *GenderFacet) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, genderFacetImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("GenderFacet") - case "gender": - out.Values[i] = ec._GenderFacet_gender(ctx, field, obj) + case "description": + out.Values[i] = ec._ImageTypeGroup_description(ctx, field, obj) + case "sort_order": + out.Values[i] = ec._ImageTypeGroup_sort_order(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "count": - out.Values[i] = ec._GenderFacet_count(ctx, field, obj) + case "exclusive": + out.Values[i] = ec._ImageTypeGroup_exclusive(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) - - for label, dfs := range deferred { - ec.ProcessDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - -var imageImplementors = []string{"Image"} - -func (ec *executionContext) _Image(ctx context.Context, sel ast.SelectionSet, obj *Image) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, imageImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("Image") - case "id": - out.Values[i] = ec._Image_id(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) - } - case "url": - field := field - - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._Image_url(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } - return res - } - - if field.Deferrable != nil { - dfs, ok := deferred[field.Deferrable.Label] - di := 0 - if ok { - dfs.AddField(field) - di = len(dfs.Values) - 1 - } else { - dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) - deferred[field.Deferrable.Label] = dfs - } - dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { - return innerFunc(ctx, dfs) - }) - - // don't run the out.Concurrently() call below - out.Values[i] = graphql.Null - continue - } - - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "width": - out.Values[i] = ec._Image_width(ctx, field, obj) + case "enabled": + out.Values[i] = ec._ImageTypeGroup_enabled(ctx, field, obj) if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + out.Invalids++ } - case "height": - out.Values[i] = ec._Image_height(ctx, field, obj) + case "types": + out.Values[i] = ec._ImageTypeGroup_types(ctx, field, obj) if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) @@ -36389,6 +39715,20 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { out.Invalids++ } + case "imageTypeOrderUpdate": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_imageTypeOrderUpdate(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "imageTypeSetEnabled": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_imageTypeSetEnabled(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } case "regenerateAPIKey": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Mutation_regenerateAPIKey(ctx, field) @@ -36620,6 +39960,13 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { out.Invalids++ } + case "updateImageTypePreferences": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_updateImageTypePreferences(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -36762,7 +40109,127 @@ func (ec *executionContext) _Notification(ctx context.Context, sel ast.Selection } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "data": + case "data": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Notification_data(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var performerImplementors = []string{"Performer", "EditTarget", "SceneDraftPerformer"} + +func (ec *executionContext) _Performer(ctx context.Context, sel ast.SelectionSet, obj *Performer) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, performerImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Performer") + case "id": + out.Values[i] = ec._Performer_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "name": + out.Values[i] = ec._Performer_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "disambiguation": + out.Values[i] = ec._Performer_disambiguation(ctx, field, obj) + case "aliases": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Performer_aliases(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "gender": + out.Values[i] = ec._Performer_gender(ctx, field, obj) + case "urls": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -36771,7 +40238,7 @@ func (ec *executionContext) _Notification(ctx context.Context, sel ast.Selection ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Notification_data(ctx, field, obj) + res = ec._Performer_urls(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -36798,65 +40265,16 @@ func (ec *executionContext) _Notification(ctx context.Context, sel ast.Selection } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) - - for label, dfs := range deferred { - ec.ProcessDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - -var performerImplementors = []string{"Performer", "EditTarget", "SceneDraftPerformer"} - -func (ec *executionContext) _Performer(ctx context.Context, sel ast.SelectionSet, obj *Performer) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, performerImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("Performer") - case "id": - out.Values[i] = ec._Performer_id(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) - } - case "name": - out.Values[i] = ec._Performer_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) - } - case "disambiguation": - out.Values[i] = ec._Performer_disambiguation(ctx, field, obj) - case "aliases": + case "birthdate": field := field - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Performer_aliases(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } + res = ec._Performer_birthdate(ctx, field, obj) return res } @@ -36880,21 +40298,20 @@ func (ec *executionContext) _Performer(ctx context.Context, sel ast.SelectionSet } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "gender": - out.Values[i] = ec._Performer_gender(ctx, field, obj) - case "urls": + case "birth_date": + out.Values[i] = ec._Performer_birth_date(ctx, field, obj) + case "death_date": + out.Values[i] = ec._Performer_death_date(ctx, field, obj) + case "age": field := field - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Performer_urls(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } + res = ec._Performer_age(ctx, field, obj) return res } @@ -36918,16 +40335,29 @@ func (ec *executionContext) _Performer(ctx context.Context, sel ast.SelectionSet } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "birthdate": + case "ethnicity": + out.Values[i] = ec._Performer_ethnicity(ctx, field, obj) + case "country": + out.Values[i] = ec._Performer_country(ctx, field, obj) + case "eye_color": + out.Values[i] = ec._Performer_eye_color(ctx, field, obj) + case "hair_color": + out.Values[i] = ec._Performer_hair_color(ctx, field, obj) + case "height": + out.Values[i] = ec._Performer_height(ctx, field, obj) + case "measurements": field := field - innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Performer_birthdate(ctx, field, obj) + res = ec._Performer_measurements(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } return res } @@ -36951,11 +40381,21 @@ func (ec *executionContext) _Performer(ctx context.Context, sel ast.SelectionSet } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "birth_date": - out.Values[i] = ec._Performer_birth_date(ctx, field, obj) - case "death_date": - out.Values[i] = ec._Performer_death_date(ctx, field, obj) - case "age": + case "cup_size": + out.Values[i] = ec._Performer_cup_size(ctx, field, obj) + case "band_size": + out.Values[i] = ec._Performer_band_size(ctx, field, obj) + case "waist_size": + out.Values[i] = ec._Performer_waist_size(ctx, field, obj) + case "hip_size": + out.Values[i] = ec._Performer_hip_size(ctx, field, obj) + case "breast_type": + out.Values[i] = ec._Performer_breast_type(ctx, field, obj) + case "career_start_year": + out.Values[i] = ec._Performer_career_start_year(ctx, field, obj) + case "career_end_year": + out.Values[i] = ec._Performer_career_end_year(ctx, field, obj) + case "tattoos": field := field innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { @@ -36964,7 +40404,7 @@ func (ec *executionContext) _Performer(ctx context.Context, sel ast.SelectionSet ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Performer_age(ctx, field, obj) + res = ec._Performer_tattoos(ctx, field, obj) return res } @@ -36988,29 +40428,16 @@ func (ec *executionContext) _Performer(ctx context.Context, sel ast.SelectionSet } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "ethnicity": - out.Values[i] = ec._Performer_ethnicity(ctx, field, obj) - case "country": - out.Values[i] = ec._Performer_country(ctx, field, obj) - case "eye_color": - out.Values[i] = ec._Performer_eye_color(ctx, field, obj) - case "hair_color": - out.Values[i] = ec._Performer_hair_color(ctx, field, obj) - case "height": - out.Values[i] = ec._Performer_height(ctx, field, obj) - case "measurements": + case "piercings": field := field - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Performer_measurements(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } + res = ec._Performer_piercings(ctx, field, obj) return res } @@ -37034,30 +40461,19 @@ func (ec *executionContext) _Performer(ctx context.Context, sel ast.SelectionSet } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "cup_size": - out.Values[i] = ec._Performer_cup_size(ctx, field, obj) - case "band_size": - out.Values[i] = ec._Performer_band_size(ctx, field, obj) - case "waist_size": - out.Values[i] = ec._Performer_waist_size(ctx, field, obj) - case "hip_size": - out.Values[i] = ec._Performer_hip_size(ctx, field, obj) - case "breast_type": - out.Values[i] = ec._Performer_breast_type(ctx, field, obj) - case "career_start_year": - out.Values[i] = ec._Performer_career_start_year(ctx, field, obj) - case "career_end_year": - out.Values[i] = ec._Performer_career_end_year(ctx, field, obj) - case "tattoos": + case "images": field := field - innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Performer_tattoos(ctx, field, obj) + res = ec._Performer_images(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } return res } @@ -37081,16 +40497,19 @@ func (ec *executionContext) _Performer(ctx context.Context, sel ast.SelectionSet } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "piercings": + case "typed_images": field := field - innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Performer_piercings(ctx, field, obj) + res = ec._Performer_typed_images(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } return res } @@ -37114,19 +40533,16 @@ func (ec *executionContext) _Performer(ctx context.Context, sel ast.SelectionSet } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "images": + case "thumbnail": field := field - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Performer_images(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } + res = ec._Performer_thumbnail(ctx, field, obj) return res } @@ -37783,17 +41199,95 @@ func (ec *executionContext) _PerformerEdit(ctx context.Context, sel ast.Selectio } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "height": - out.Values[i] = ec._PerformerEdit_height(ctx, field, obj) - case "cup_size": - out.Values[i] = ec._PerformerEdit_cup_size(ctx, field, obj) - case "band_size": - out.Values[i] = ec._PerformerEdit_band_size(ctx, field, obj) - case "waist_size": - out.Values[i] = ec._PerformerEdit_waist_size(ctx, field, obj) - case "hip_size": - out.Values[i] = ec._PerformerEdit_hip_size(ctx, field, obj) - case "breast_type": + case "height": + out.Values[i] = ec._PerformerEdit_height(ctx, field, obj) + case "cup_size": + out.Values[i] = ec._PerformerEdit_cup_size(ctx, field, obj) + case "band_size": + out.Values[i] = ec._PerformerEdit_band_size(ctx, field, obj) + case "waist_size": + out.Values[i] = ec._PerformerEdit_waist_size(ctx, field, obj) + case "hip_size": + out.Values[i] = ec._PerformerEdit_hip_size(ctx, field, obj) + case "breast_type": + field := field + + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._PerformerEdit_breast_type(ctx, field, obj) + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "career_start_year": + out.Values[i] = ec._PerformerEdit_career_start_year(ctx, field, obj) + case "career_end_year": + out.Values[i] = ec._PerformerEdit_career_end_year(ctx, field, obj) + case "added_tattoos": + out.Values[i] = ec._PerformerEdit_added_tattoos(ctx, field, obj) + case "removed_tattoos": + out.Values[i] = ec._PerformerEdit_removed_tattoos(ctx, field, obj) + case "added_piercings": + out.Values[i] = ec._PerformerEdit_added_piercings(ctx, field, obj) + case "removed_piercings": + out.Values[i] = ec._PerformerEdit_removed_piercings(ctx, field, obj) + case "added_images": + field := field + + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._PerformerEdit_added_images(ctx, field, obj) + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "removed_images": field := field innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { @@ -37802,7 +41296,7 @@ func (ec *executionContext) _PerformerEdit(ctx context.Context, sel ast.Selectio ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._PerformerEdit_breast_type(ctx, field, obj) + res = ec._PerformerEdit_removed_images(ctx, field, obj) return res } @@ -37826,28 +41320,19 @@ func (ec *executionContext) _PerformerEdit(ctx context.Context, sel ast.Selectio } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "career_start_year": - out.Values[i] = ec._PerformerEdit_career_start_year(ctx, field, obj) - case "career_end_year": - out.Values[i] = ec._PerformerEdit_career_end_year(ctx, field, obj) - case "added_tattoos": - out.Values[i] = ec._PerformerEdit_added_tattoos(ctx, field, obj) - case "removed_tattoos": - out.Values[i] = ec._PerformerEdit_removed_tattoos(ctx, field, obj) - case "added_piercings": - out.Values[i] = ec._PerformerEdit_added_piercings(ctx, field, obj) - case "removed_piercings": - out.Values[i] = ec._PerformerEdit_removed_piercings(ctx, field, obj) - case "added_images": + case "image_changes": field := field - innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._PerformerEdit_added_images(ctx, field, obj) + res = ec._PerformerEdit_image_changes(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } return res } @@ -37871,16 +41356,19 @@ func (ec *executionContext) _PerformerEdit(ctx context.Context, sel ast.Selectio } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "removed_images": + case "typed_images": field := field - innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._PerformerEdit_removed_images(ctx, field, obj) + res = ec._PerformerEdit_typed_images(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } return res } @@ -38692,6 +42180,28 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "imageTypeGroups": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_imageTypeGroups(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "findEdit": field := field @@ -42744,6 +46254,52 @@ func (ec *executionContext) _TagEdit(ctx context.Context, sel ast.SelectionSet, return out } +var typedImageImplementors = []string{"TypedImage"} + +func (ec *executionContext) _TypedImage(ctx context.Context, sel ast.SelectionSet, obj *TypedImage) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, typedImageImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("TypedImage") + case "image": + out.Values[i] = ec._TypedImage_image(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "types": + out.Values[i] = ec._TypedImage_types(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "date": + out.Values[i] = ec._TypedImage_date(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var uRLImplementors = []string{"URL"} func (ec *executionContext) _URL(ctx context.Context, sel ast.SelectionSet, obj *URL) graphql.Marshaler { @@ -43031,6 +46587,78 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj continue } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "image_type_preferences": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._User_image_type_preferences(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "image_type_group_preferences": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._User_image_type_group_preferences(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "vote_count": field := field @@ -43921,6 +47549,106 @@ func (ec *executionContext) marshalNCriterionModifier2githubᚗcomᚋstashappᚋ return v } +func (ec *executionContext) marshalNCropGuide2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropGuide(ctx context.Context, sel ast.SelectionSet, v CropGuide) graphql.Marshaler { + return ec._CropGuide(ctx, sel, &v) +} + +func (ec *executionContext) marshalNCropGuide2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropGuideᚄ(ctx context.Context, sel ast.SelectionSet, v []CropGuide) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalNCropGuide2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropGuide(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) unmarshalNCropGuideAxisEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropGuideAxisEnum(ctx context.Context, v any) (CropGuideAxisEnum, error) { + var res CropGuideAxisEnum + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNCropGuideAxisEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropGuideAxisEnum(ctx context.Context, sel ast.SelectionSet, v CropGuideAxisEnum) graphql.Marshaler { + return v +} + +func (ec *executionContext) marshalNCropKnot2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropKnot(ctx context.Context, sel ast.SelectionSet, v CropKnot) graphql.Marshaler { + return ec._CropKnot(ctx, sel, &v) +} + +func (ec *executionContext) marshalNCropKnot2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropKnotᚄ(ctx context.Context, sel ast.SelectionSet, v []CropKnot) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalNCropKnot2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropKnot(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNCropPoint2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropPoint(ctx context.Context, sel ast.SelectionSet, v *CropPoint) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._CropPoint(ctx, sel, v) +} + +func (ec *executionContext) marshalNCropShape2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropShape(ctx context.Context, sel ast.SelectionSet, v CropShape) graphql.Marshaler { + return ec._CropShape(ctx, sel, &v) +} + +func (ec *executionContext) marshalNCropShape2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropShapeᚄ(ctx context.Context, sel ast.SelectionSet, v []CropShape) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalNCropShape2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropShape(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNCropSubpath2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropSubpath(ctx context.Context, sel ast.SelectionSet, v CropSubpath) graphql.Marshaler { + return ec._CropSubpath(ctx, sel, &v) +} + +func (ec *executionContext) marshalNCropSubpath2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropSubpathᚄ(ctx context.Context, sel ast.SelectionSet, v []CropSubpath) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalNCropSubpath2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropSubpath(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + func (ec *executionContext) unmarshalNDate2string(ctx context.Context, v any) (string, error) { res, err := graphql.UnmarshalString(v) return res, graphql.ErrorOnPath(ctx, err) @@ -44407,6 +48135,22 @@ func (ec *executionContext) marshalNFingerprintSubmissionResult2ᚕgithubᚗcom return ret } +func (ec *executionContext) unmarshalNFloat2float64(ctx context.Context, v any) (float64, error) { + res, err := graphql.UnmarshalFloatContext(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNFloat2float64(ctx context.Context, sel ast.SelectionSet, v float64) graphql.Marshaler { + _ = sel + res := graphql.MarshalFloatContext(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + } + return graphql.WrapContextMarshaler(ctx, res) +} + func (ec *executionContext) unmarshalNGenderEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐGenderEnum(ctx context.Context, v any) (GenderEnum, error) { var res GenderEnum err := res.UnmarshalGQL(v) @@ -44513,6 +48257,41 @@ func (ec *executionContext) marshalNImage2ᚕgithubᚗcomᚋstashappᚋstashᚑb return ret } +func (ec *executionContext) marshalNImage2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImage(ctx context.Context, sel ast.SelectionSet, v *Image) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Image(ctx, sel, v) +} + +func (ec *executionContext) marshalNImageAssignmentChange2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageAssignmentChange(ctx context.Context, sel ast.SelectionSet, v ImageAssignmentChange) graphql.Marshaler { + return ec._ImageAssignmentChange(ctx, sel, &v) +} + +func (ec *executionContext) marshalNImageAssignmentChange2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageAssignmentChangeᚄ(ctx context.Context, sel ast.SelectionSet, v []ImageAssignmentChange) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalNImageAssignmentChange2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageAssignmentChange(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) unmarshalNImageAssignmentInput2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageAssignmentInput(ctx context.Context, v any) (ImageAssignmentInput, error) { + res, err := ec.unmarshalInputImageAssignmentInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + func (ec *executionContext) unmarshalNImageCreateInput2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageCreateInput(ctx context.Context, v any) (ImageCreateInput, error) { res, err := ec.unmarshalInputImageCreateInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) @@ -44523,6 +48302,184 @@ func (ec *executionContext) unmarshalNImageDestroyInput2githubᚗcomᚋstashapp return res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) marshalNImageType2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageType(ctx context.Context, sel ast.SelectionSet, v ImageType) graphql.Marshaler { + return ec._ImageType(ctx, sel, &v) +} + +func (ec *executionContext) marshalNImageType2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeᚄ(ctx context.Context, sel ast.SelectionSet, v []ImageType) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalNImageType2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageType(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) unmarshalNImageTypeEnabledInput2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnabledInput(ctx context.Context, v any) (ImageTypeEnabledInput, error) { + res, err := ec.unmarshalInputImageTypeEnabledInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) unmarshalNImageTypeEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnum(ctx context.Context, v any) (ImageTypeEnum, error) { + var res ImageTypeEnum + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNImageTypeEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnum(ctx context.Context, sel ast.SelectionSet, v ImageTypeEnum) graphql.Marshaler { + return v +} + +func (ec *executionContext) unmarshalNImageTypeEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnumᚄ(ctx context.Context, v any) ([]ImageTypeEnum, error) { + var vSlice []any + vSlice = graphql.CoerceList(v) + var err error + res := make([]ImageTypeEnum, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalNImageTypeEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnum(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalNImageTypeEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnumᚄ(ctx context.Context, sel ast.SelectionSet, v []ImageTypeEnum) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalNImageTypeEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeEnum(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNImageTypeGroup2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroup(ctx context.Context, sel ast.SelectionSet, v ImageTypeGroup) graphql.Marshaler { + return ec._ImageTypeGroup(ctx, sel, &v) +} + +func (ec *executionContext) marshalNImageTypeGroup2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupᚄ(ctx context.Context, sel ast.SelectionSet, v []ImageTypeGroup) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalNImageTypeGroup2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroup(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) unmarshalNImageTypeGroupEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupEnum(ctx context.Context, v any) (ImageTypeGroupEnum, error) { + var res ImageTypeGroupEnum + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNImageTypeGroupEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupEnum(ctx context.Context, sel ast.SelectionSet, v ImageTypeGroupEnum) graphql.Marshaler { + return v +} + +func (ec *executionContext) unmarshalNImageTypeGroupEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupEnumᚄ(ctx context.Context, v any) ([]ImageTypeGroupEnum, error) { + var vSlice []any + vSlice = graphql.CoerceList(v) + var err error + res := make([]ImageTypeGroupEnum, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalNImageTypeGroupEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupEnum(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalNImageTypeGroupEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupEnumᚄ(ctx context.Context, sel ast.SelectionSet, v []ImageTypeGroupEnum) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalNImageTypeGroupEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupEnum(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) unmarshalNImageTypeOrderInput2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeOrderInput(ctx context.Context, v any) (ImageTypeOrderInput, error) { + res, err := ec.unmarshalInputImageTypeOrderInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) unmarshalNImageTypePreferencesInput2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypePreferencesInput(ctx context.Context, v any) (ImageTypePreferencesInput, error) { + res, err := ec.unmarshalInputImageTypePreferencesInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) unmarshalNImageTypeScopeEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeScopeEnum(ctx context.Context, v any) (ImageTypeScopeEnum, error) { + var res ImageTypeScopeEnum + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNImageTypeScopeEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeScopeEnum(ctx context.Context, sel ast.SelectionSet, v ImageTypeScopeEnum) graphql.Marshaler { + return v +} + +func (ec *executionContext) unmarshalNImageTypeScopeEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeScopeEnumᚄ(ctx context.Context, v any) ([]ImageTypeScopeEnum, error) { + var vSlice []any + vSlice = graphql.CoerceList(v) + var err error + res := make([]ImageTypeScopeEnum, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalNImageTypeScopeEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeScopeEnum(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalNImageTypeScopeEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeScopeEnumᚄ(ctx context.Context, sel ast.SelectionSet, v []ImageTypeScopeEnum) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalNImageTypeScopeEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeScopeEnum(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v any) (int, error) { res, err := graphql.UnmarshalInt(v) return res, graphql.ErrorOnPath(ctx, err) @@ -45631,6 +49588,26 @@ func (ec *executionContext) marshalNTime2ᚖtimeᚐTime(ctx context.Context, sel return res } +func (ec *executionContext) marshalNTypedImage2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐTypedImage(ctx context.Context, sel ast.SelectionSet, v TypedImage) graphql.Marshaler { + return ec._TypedImage(ctx, sel, &v) +} + +func (ec *executionContext) marshalNTypedImage2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐTypedImageᚄ(ctx context.Context, sel ast.SelectionSet, v []TypedImage) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalNTypedImage2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐTypedImage(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + func (ec *executionContext) marshalNURL2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐURL(ctx context.Context, sel ast.SelectionSet, v URL) graphql.Marshaler { return ec._URL(ctx, sel, &v) } @@ -46091,6 +50068,29 @@ func (ec *executionContext) marshalOBreastTypeEnum2ᚖgithubᚗcomᚋstashappᚋ return v } +func (ec *executionContext) unmarshalOCropGuideRoleEnum2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropGuideRoleEnum(ctx context.Context, v any) (*CropGuideRoleEnum, error) { + if v == nil { + return nil, nil + } + var res = new(CropGuideRoleEnum) + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOCropGuideRoleEnum2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropGuideRoleEnum(ctx context.Context, sel ast.SelectionSet, v *CropGuideRoleEnum) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return v +} + +func (ec *executionContext) marshalOCropTemplate2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐCropTemplate(ctx context.Context, sel ast.SelectionSet, v *CropTemplate) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._CropTemplate(ctx, sel, v) +} + func (ec *executionContext) unmarshalODateCriterionInput2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐDateCriterionInput(ctx context.Context, v any) (*DateCriterionInput, error) { if v == nil { return nil, nil @@ -46296,6 +50296,23 @@ func (ec *executionContext) marshalOFingerprintSubmissionType2ᚖgithubᚗcomᚋ return v } +func (ec *executionContext) unmarshalOFloat2ᚖfloat64(ctx context.Context, v any) (*float64, error) { + if v == nil { + return nil, nil + } + res, err := graphql.UnmarshalFloatContext(ctx, v) + return &res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOFloat2ᚖfloat64(ctx context.Context, sel ast.SelectionSet, v *float64) graphql.Marshaler { + if v == nil { + return graphql.Null + } + _ = sel + res := graphql.MarshalFloatContext(*v) + return graphql.WrapContextMarshaler(ctx, res) +} + func (ec *executionContext) marshalOFuzzyDate2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐFuzzyDate(ctx context.Context, sel ast.SelectionSet, v *FuzzyDate) graphql.Marshaler { if v == nil { return graphql.Null @@ -46455,6 +50472,85 @@ func (ec *executionContext) marshalOImage2ᚖgithubᚗcomᚋstashappᚋstashᚑb return ec._Image(ctx, sel, v) } +func (ec *executionContext) unmarshalOImageAssignmentInput2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageAssignmentInputᚄ(ctx context.Context, v any) ([]ImageAssignmentInput, error) { + if v == nil { + return nil, nil + } + var vSlice []any + vSlice = graphql.CoerceList(v) + var err error + res := make([]ImageAssignmentInput, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalNImageAssignmentInput2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageAssignmentInput(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) unmarshalOImageCropInput2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageCropInput(ctx context.Context, v any) (*ImageCropInput, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalInputImageCropInput(ctx, v) + return &res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) unmarshalOImageTypeGroupEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupEnumᚄ(ctx context.Context, v any) ([]ImageTypeGroupEnum, error) { + if v == nil { + return nil, nil + } + var vSlice []any + vSlice = graphql.CoerceList(v) + var err error + res := make([]ImageTypeGroupEnum, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalNImageTypeGroupEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupEnum(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalOImageTypeGroupEnum2ᚕgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupEnumᚄ(ctx context.Context, sel ast.SelectionSet, v []ImageTypeGroupEnum) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalNImageTypeGroupEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeGroupEnum(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) unmarshalOImageTypeScopeEnum2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeScopeEnum(ctx context.Context, v any) (*ImageTypeScopeEnum, error) { + if v == nil { + return nil, nil + } + var res = new(ImageTypeScopeEnum) + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOImageTypeScopeEnum2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋinternalᚋmodelsᚐImageTypeScopeEnum(ctx context.Context, sel ast.SelectionSet, v *ImageTypeScopeEnum) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return v +} + func (ec *executionContext) unmarshalOInt2int(ctx context.Context, v any) (int, error) { res, err := graphql.UnmarshalInt(v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/internal/models/generated_models.go b/internal/models/generated_models.go index 5259905e5..aea35a2b5 100644 --- a/internal/models/generated_models.go +++ b/internal/models/generated_models.go @@ -123,6 +123,90 @@ type CommentVotedEdit struct { func (CommentVotedEdit) IsNotificationData() {} +// One guide line of a crop template +type CropGuide struct { + Axis CropGuideAxisEnum `json:"axis"` + // Where the line sits, as a fraction of the canvas along its axis: 0 is the + // left or top edge, 1 the right or bottom. A fraction rather than a pixel + // because a template is drawn at one size and rendered at every other + Position float64 `json:"position"` + // How closely the line is meant to be followed, where the template says. An + // anchor is meant to be hit; a reference is for judgement and balance + Role *CropGuideRoleEnum `json:"role,omitempty"` + // What the line is for, like "bisects the eyes", "where the thighs meet", or + // null when the template does not name it + Label *string `json:"label,omitempty"` + // Whether a frame is resized around this line when the contributor holds + // Shift + // + // Independent of `role`, which says how closely a line is meant to be + // followed. A headshot's eye line is the softest line in its template (like the + // head and chin can be hard limits) and is still the right thing to turn a + // resize about, so the two cannot be the same field + // + // At most one guide per axis carries it. A template naming none on an axis + // resizes about the centre there + Pivot bool `json:"pivot"` +} + +// One anchor of an outline, with the control point either side of it +// +// Every segment is a cubic curve, including straight ones. Photoshop draws a +// straight edge as a curve whose controls sit on its anchors, so a rectangle and +// an ellipse arrive in the same shape +type CropKnot struct { + // The control point governing the curve arriving at this anchor + ControlIn *CropPoint `json:"control_in"` + Anchor *CropPoint `json:"anchor"` + // The control point governing the curve leaving it + ControlOut *CropPoint `json:"control_out"` +} + +// A position on the template's canvas, as fractions of its width and height +// +// Fractions like a guide's position, and for the same reason: a template is drawn +// at one size and rendered at every other. Values outside 0 to 1 are legitimate: +// a crop box is often drawn a hair outside the canvas so its stroke does not eat +// into the picture +type CropPoint struct { + X float64 `json:"x"` + Y float64 `json:"y"` +} + +// One outline drawn in a crop template +type CropShape struct { + // What the template's author called the layer, like "head guide", "eyes soft + // anchor", or null for an unnamed layer + Label *string `json:"label,omitempty"` + Subpaths []CropSubpath `json:"subpaths"` +} + +// One continuous run of a shape's outline +// +// A shape can be several: a ring is an outer subpath and an inner one, and +// whether each closes back on itself is the difference between an outline and an +// arc +type CropSubpath struct { + Closed bool `json:"closed"` + Knots []CropKnot `json:"knots"` +} + +// A crop frame, read from a Photoshop template +// +// The template file is the source of truth: the guides drawn over the cropping +// tool and the .psd a contributor can download for their own editor are the same +// bytes, so the two cannot drift +type CropTemplate struct { + // Width over height, taken from the template's canvas rather than set + // anywhere + AspectRatio float64 `json:"aspect_ratio"` + Guides []CropGuide `json:"guides"` + // Outlines drawn on the template's own layers like an oval for a face to sit + // inside, a bar marking a margin. Guidance only: the crop is still a + // rectangle, and nothing here changes what the server cuts + Shapes []CropShape `json:"shapes"` +} + type DateCriterionInput struct { Value string `json:"value"` Modifier CriterionModifier `json:"modifier"` @@ -376,15 +460,170 @@ type IDCriterionInput struct { Modifier CriterionModifier `json:"modifier"` } +// What an edit changes about one image's labels and date, grouped by image +// rather than listed as flat added/removed tuples: one performer edit can +// relabel a whole gallery. +type ImageAssignmentChange struct { + Image *Image `json:"image"` + AddedTypes []ImageTypeEnum `json:"added_types"` + RemovedTypes []ImageTypeEnum `json:"removed_types"` + // The date this edit sets. Only meaningful when date_changed is true, where a + // null means the edit clears the date. + Date *string `json:"date,omitempty"` + // Whether this edit changes the image's date at all. + DateChanged bool `json:"date_changed"` +} + +// Everything said about one image's presence on an entity. An entry whose types +// are empty clears that image's labels. +// +// **What each way of sending `image_types` means.** Three write paths implement +// this: `performerCreate` and `performerUpdate` in Go, and the edit path in Go +// at submission and SQL at apply. Nothing makes them agree but this table. +// +// | `image_types` is | performerCreate | performerUpdate | edit | +// |---|---|---|---| +// | absent | unlabelled | preserves all | preserves all | +// | explicit `null` | unlabelled | **preserves all** | **clears all** | +// | `[]` | unlabelled | clears all | clears all | +// | non-empty | labels the images named | authoritative only over the images named | authoritative only over the images named | +// +// Null differs because the edit path is told which fields the client stated and +// `performerUpdate` is not. **Send `[]` to clear on any path** and the question +// does not arise. +// +// Note that a non-empty list leaves an image it does not mention exactly as it +// was; otherwise every client touching `image_ids` would have to restate the +// whole gallery's labels or destroy them. +// +// **And the same for `date`,** which is single-valued and so overrides +// rather than merges: +// +// | the submission | the image's date | +// |---|---| +// | no entry for this image | kept | +// | an entry stating `date` | set | +// | an entry omitting `date` | cleared, see the field's own note | +// | an entry omitting it, on an image being added | stays empty, and is not reported as a change | +type ImageAssignmentInput struct { + ImageID uuid.UUID `json:"image_id"` + Types []ImageTypeEnum `json:"types"` + // When the image is from. Partial ISO 8601: 2019, 2019-06, or 2019-06-15. + // + // An entry states the whole of what is true about its image, so omitting this + // clears the date rather than leaving it. Send the current value back if the + // change is only to the labels. + Date *string `json:"date,omitempty"` +} + type ImageCreateInput struct { URL *string `json:"url,omitempty"` File *graphql.Upload `json:"file,omitempty"` + Crop *ImageCropInput `json:"crop,omitempty"` +} + +// A frame to cut an upload down to, in the coordinates the client is looking at. +// +// Cropping happens here rather than in the browser for two reasons. A canvas +// re-encode is a second lossy generation on top of whatever the contributor +// started with, where the server decodes once and encodes once. And images are +// deduplicated on a checksum of their stored bytes, which stops working if the +// bytes are produced by whichever encoder the uploader's browser happens to +// have: two people cropping the same source to the same frame would land as two +// images +type ImageCropInput struct { + // Distance from the left edge, as a fraction of the width + X float64 `json:"x"` + // Distance from the top edge, as a fraction of the height + Y float64 `json:"y"` + // Fraction of the width to keep + Width float64 `json:"width"` + // Fraction of the height to keep + Height float64 `json:"height"` + // Degrees to rotate clockwise before cutting, for a tilted horizon. The frame + // above is measured against the rotated image, which is larger than the + // original - the same thing the client is dragging over. + // + // EXIF orientation is applied before any of this, so the coordinates are the + // ones a browser shows rather than the ones stored in the file + Angle *float64 `json:"angle,omitempty"` } type ImageDestroyInput struct { ID uuid.UUID `json:"id"` } +type ImageType struct { + Key ImageTypeEnum `json:"key"` + Name string `json:"name"` + Description *string `json:"description,omitempty"` + // Value priority within the group; lower wins + SortOrder int `json:"sort_order"` + ValidTypes []ImageTypeScopeEnum `json:"valid_types"` + // Whether this instance uses this type. Disabled types cannot be assigned. + Enabled bool `json:"enabled"` + // Types this one cannot share an image with, across groups: a face crop cannot + // be topless, because the chest is not in frame. Symmetric: each side + // of a pair lists the other. Assigning both is rejected; a client should stop + // offering the second once the first is chosen. + ConflictsWith []ImageTypeEnum `json:"conflicts_with"` + // The frame to crop to for this type, or null if the instance has no template + // for it. Only crops have one - nothing about a pose or a state of dress says + // anything about the shape of the picture + CropTemplate *CropTemplate `json:"crop_template,omitempty"` +} + +// Which parts of the vocabulary an instance switches off. +// +// Expressed as what is disabled rather than what is enabled, so a type added to +// the taxonomy later arrives switched on. +type ImageTypeEnabledInput struct { + // Groups to switch off. A group being off implies its types are too. + DisabledGroups []ImageTypeGroupEnum `json:"disabled_groups"` + // Types to switch off individually, whatever their group's state. + DisabledTypes []ImageTypeEnum `json:"disabled_types"` +} + +type ImageTypeGroup struct { + Key ImageTypeGroupEnum `json:"key"` + Name string `json:"name"` + Description *string `json:"description,omitempty"` + // Dimension priority when ranking images; lower wins + SortOrder int `json:"sort_order"` + // At most one type from this group may be assigned to an image + Exclusive bool `json:"exclusive"` + // Whether this instance uses this dimension. A disabled group is not offered + // when labelling and takes no part in ranking; existing assignments are kept, + // so re-enabling restores them. + Enabled bool `json:"enabled"` + Types []ImageType `json:"types"` +} + +// A complete reordering of the vocabulary. Partial lists are rejected rather than +// merged. +type ImageTypeOrderInput struct { + // Groups in priority order. Must list every group exactly once. + Groups []ImageTypeGroupEnum `json:"groups"` + // Types in priority order. Must list every type exactly once. Only position + // within each group counts, so types of different groups may interleave freely. + Types []ImageTypeEnum `json:"types"` +} + +// One user's ranking. Unlike the admin ordering both lists may be partial: a user +// says what they care about and everything else keeps the instance order behind +// it, which is what lets someone express "nudes first" without having to rank all +// seventeen types. +type ImageTypePreferencesInput struct { + // Types in preferred order, position within each group being what counts. + Types []ImageTypeEnum `json:"types"` + // Groups in preferred order, deciding which dimension is compared first. + // + // Absent leaves the group preference as it is; an empty list clears it. Not + // defaulted, so a client sending only `types` keeps the group ordering it did + // not mention. + Groups []ImageTypeGroupEnum `json:"groups,omitempty"` +} + type ImageUpdateInput struct { ID uuid.UUID `json:"id"` URL *string `json:"url,omitempty"` @@ -473,7 +712,10 @@ type PerformerCreateInput struct { Tattoos []BodyModificationInput `json:"tattoos,omitempty"` Piercings []BodyModificationInput `json:"piercings,omitempty"` ImageIds []uuid.UUID `json:"image_ids,omitempty"` - DraftID *uuid.UUID `json:"draft_id,omitempty"` + // Labels for the images named. An image in image_ids with no entry here is + // simply unlabelled; there is nothing to preserve on a create. + ImageTypes []ImageAssignmentInput `json:"image_types,omitempty"` + DraftID *uuid.UUID `json:"draft_id,omitempty"` } type PerformerDestroyInput struct { @@ -526,7 +768,15 @@ type PerformerEditDetailsInput struct { Tattoos []BodyModificationInput `json:"tattoos,omitempty"` Piercings []BodyModificationInput `json:"piercings,omitempty"` ImageIds []uuid.UUID `json:"image_ids,omitempty"` - DraftID *uuid.UUID `json:"draft_id,omitempty"` + // Labels for the images named. Omitting the field leaves assignments alone; + // null or an empty list clears them all, matching image_ids. A non-empty list + // is authoritative only over the images it names. + // + // Null clearing here and preserving on performerUpdate is not a rule, it is + // what each path can see: this one is told which fields the client stated, and + // that one is not. Send an empty list to clear, on either path. + ImageTypes []ImageAssignmentInput `json:"image_types,omitempty"` + DraftID *uuid.UUID `json:"draft_id,omitempty"` } type PerformerEditInput struct { @@ -635,6 +885,15 @@ type PerformerUpdateInput struct { Tattoos []BodyModificationInput `json:"tattoos,omitempty"` Piercings []BodyModificationInput `json:"piercings,omitempty"` ImageIds []uuid.UUID `json:"image_ids,omitempty"` + // Labels for the images named. Absent leaves every assignment untouched, an + // empty list clears them all, and an image in image_ids with no entry here + // keeps what it has. + // + // Explicit null behaves as absent and preserves, which differs from the edit + // path, where it clears. This path is not told which fields the client stated, + // so it cannot tell an omitted field from one set to null; the edit path is, + // and does. Send an empty list to clear, on either path. + ImageTypes []ImageAssignmentInput `json:"image_types,omitempty"` } // The query root for this schema @@ -1005,6 +1264,15 @@ type TagUpdateInput struct { CategoryID *uuid.UUID `json:"category_id,omitempty"` } +// An image together with what it has been labelled on this entity. Not +// performer-specific: scenes and studios expose the same type. +type TypedImage struct { + Image *Image `json:"image"` + Types []ImageTypeEnum `json:"types"` + // When the image is from. Partial ISO 8601: 2019, 2019-06, or 2019-06-15. + Date *string `json:"date,omitempty"` +} + type UnreadNotificationCount struct { Total int `json:"total"` Urgent int `json:"urgent"` @@ -1249,6 +1517,120 @@ func (e CriterionModifier) MarshalJSON() ([]byte, error) { return buf.Bytes(), nil } +type CropGuideAxisEnum string + +const ( + // A vertical line, positioned across the width + CropGuideAxisEnumX CropGuideAxisEnum = "X" + // A horizontal line, positioned down the height + CropGuideAxisEnumY CropGuideAxisEnum = "Y" +) + +var AllCropGuideAxisEnum = []CropGuideAxisEnum{ + CropGuideAxisEnumX, + CropGuideAxisEnumY, +} + +func (e CropGuideAxisEnum) IsValid() bool { + switch e { + case CropGuideAxisEnumX, CropGuideAxisEnumY: + return true + } + return false +} + +func (e CropGuideAxisEnum) String() string { + return string(e) +} + +func (e *CropGuideAxisEnum) UnmarshalGQL(v any) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = CropGuideAxisEnum(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid CropGuideAxisEnum", str) + } + return nil +} + +func (e CropGuideAxisEnum) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} + +func (e *CropGuideAxisEnum) UnmarshalJSON(b []byte) error { + s, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + return e.UnmarshalGQL(s) +} + +func (e CropGuideAxisEnum) MarshalJSON() ([]byte, error) { + var buf bytes.Buffer + e.MarshalGQL(&buf) + return buf.Bytes(), nil +} + +type CropGuideRoleEnum string + +const ( + CropGuideRoleEnumAnchor CropGuideRoleEnum = "ANCHOR" + CropGuideRoleEnumReference CropGuideRoleEnum = "REFERENCE" + CropGuideRoleEnumMargin CropGuideRoleEnum = "MARGIN" +) + +var AllCropGuideRoleEnum = []CropGuideRoleEnum{ + CropGuideRoleEnumAnchor, + CropGuideRoleEnumReference, + CropGuideRoleEnumMargin, +} + +func (e CropGuideRoleEnum) IsValid() bool { + switch e { + case CropGuideRoleEnumAnchor, CropGuideRoleEnumReference, CropGuideRoleEnumMargin: + return true + } + return false +} + +func (e CropGuideRoleEnum) String() string { + return string(e) +} + +func (e *CropGuideRoleEnum) UnmarshalGQL(v any) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = CropGuideRoleEnum(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid CropGuideRoleEnum", str) + } + return nil +} + +func (e CropGuideRoleEnum) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} + +func (e *CropGuideRoleEnum) UnmarshalJSON(b []byte) error { + s, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + return e.UnmarshalGQL(s) +} + +func (e CropGuideRoleEnum) MarshalJSON() ([]byte, error) { + var buf bytes.Buffer + e.MarshalGQL(&buf) + return buf.Bytes(), nil +} + type DateAccuracyEnum string const ( @@ -1935,6 +2317,236 @@ func (e HairColorEnum) MarshalJSON() ([]byte, error) { return buf.Bytes(), nil } +// A label that may be applied to an image's presence on an entity. +// +// Every key is its group key followed by an underscore, so SHOT_PORTRAIT belongs +// to the SHOT group. The vocabulary is fixed and identical on every instance, +// which is what lets a client code against these values directly. +type ImageTypeEnum string + +const ( + ImageTypeEnumShotPortrait ImageTypeEnum = "SHOT_PORTRAIT" + ImageTypeEnumShotCandid ImageTypeEnum = "SHOT_CANDID" + ImageTypeEnumShotDetail ImageTypeEnum = "SHOT_DETAIL" + ImageTypeEnumCropFace ImageTypeEnum = "CROP_FACE" + ImageTypeEnumCropBust ImageTypeEnum = "CROP_BUST" + ImageTypeEnumCropThreeQuarter ImageTypeEnum = "CROP_THREE_QUARTER" + ImageTypeEnumCropThreeQuarterPlus ImageTypeEnum = "CROP_THREE_QUARTER_PLUS" + ImageTypeEnumCropFullBody ImageTypeEnum = "CROP_FULL_BODY" + ImageTypeEnumCropTorso ImageTypeEnum = "CROP_TORSO" + ImageTypeEnumCropWide ImageTypeEnum = "CROP_WIDE" + ImageTypeEnumViewFront ImageTypeEnum = "VIEW_FRONT" + ImageTypeEnumViewSide ImageTypeEnum = "VIEW_SIDE" + ImageTypeEnumViewBack ImageTypeEnum = "VIEW_BACK" + ImageTypeEnumPostureStanding ImageTypeEnum = "POSTURE_STANDING" + ImageTypeEnumPostureSitting ImageTypeEnum = "POSTURE_SITTING" + ImageTypeEnumPostureKneeling ImageTypeEnum = "POSTURE_KNEELING" + ImageTypeEnumPostureSquatting ImageTypeEnum = "POSTURE_SQUATTING" + ImageTypeEnumPostureOnAllFours ImageTypeEnum = "POSTURE_ON_ALL_FOURS" + ImageTypeEnumPostureLying ImageTypeEnum = "POSTURE_LYING" + ImageTypeEnumPostureSuspended ImageTypeEnum = "POSTURE_SUSPENDED" + ImageTypeEnumDressNonNude ImageTypeEnum = "DRESS_NON_NUDE" + ImageTypeEnumDressUnderwear ImageTypeEnum = "DRESS_UNDERWEAR" + ImageTypeEnumDressTopless ImageTypeEnum = "DRESS_TOPLESS" + ImageTypeEnumDressNude ImageTypeEnum = "DRESS_NUDE" + ImageTypeEnumDressExplicit ImageTypeEnum = "DRESS_EXPLICIT" +) + +var AllImageTypeEnum = []ImageTypeEnum{ + ImageTypeEnumShotPortrait, + ImageTypeEnumShotCandid, + ImageTypeEnumShotDetail, + ImageTypeEnumCropFace, + ImageTypeEnumCropBust, + ImageTypeEnumCropThreeQuarter, + ImageTypeEnumCropThreeQuarterPlus, + ImageTypeEnumCropFullBody, + ImageTypeEnumCropTorso, + ImageTypeEnumCropWide, + ImageTypeEnumViewFront, + ImageTypeEnumViewSide, + ImageTypeEnumViewBack, + ImageTypeEnumPostureStanding, + ImageTypeEnumPostureSitting, + ImageTypeEnumPostureKneeling, + ImageTypeEnumPostureSquatting, + ImageTypeEnumPostureOnAllFours, + ImageTypeEnumPostureLying, + ImageTypeEnumPostureSuspended, + ImageTypeEnumDressNonNude, + ImageTypeEnumDressUnderwear, + ImageTypeEnumDressTopless, + ImageTypeEnumDressNude, + ImageTypeEnumDressExplicit, +} + +func (e ImageTypeEnum) IsValid() bool { + switch e { + case ImageTypeEnumShotPortrait, ImageTypeEnumShotCandid, ImageTypeEnumShotDetail, ImageTypeEnumCropFace, ImageTypeEnumCropBust, ImageTypeEnumCropThreeQuarter, ImageTypeEnumCropThreeQuarterPlus, ImageTypeEnumCropFullBody, ImageTypeEnumCropTorso, ImageTypeEnumCropWide, ImageTypeEnumViewFront, ImageTypeEnumViewSide, ImageTypeEnumViewBack, ImageTypeEnumPostureStanding, ImageTypeEnumPostureSitting, ImageTypeEnumPostureKneeling, ImageTypeEnumPostureSquatting, ImageTypeEnumPostureOnAllFours, ImageTypeEnumPostureLying, ImageTypeEnumPostureSuspended, ImageTypeEnumDressNonNude, ImageTypeEnumDressUnderwear, ImageTypeEnumDressTopless, ImageTypeEnumDressNude, ImageTypeEnumDressExplicit: + return true + } + return false +} + +func (e ImageTypeEnum) String() string { + return string(e) +} + +func (e *ImageTypeEnum) UnmarshalGQL(v any) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = ImageTypeEnum(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid ImageTypeEnum", str) + } + return nil +} + +func (e ImageTypeEnum) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} + +func (e *ImageTypeEnum) UnmarshalJSON(b []byte) error { + s, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + return e.UnmarshalGQL(s) +} + +func (e ImageTypeEnum) MarshalJSON() ([]byte, error) { + var buf bytes.Buffer + e.MarshalGQL(&buf) + return buf.Bytes(), nil +} + +// A dimension of the image type vocabulary. Types within one group are ranked against each other. +type ImageTypeGroupEnum string + +const ( + ImageTypeGroupEnumShot ImageTypeGroupEnum = "SHOT" + ImageTypeGroupEnumCrop ImageTypeGroupEnum = "CROP" + ImageTypeGroupEnumView ImageTypeGroupEnum = "VIEW" + ImageTypeGroupEnumPosture ImageTypeGroupEnum = "POSTURE" + ImageTypeGroupEnumDress ImageTypeGroupEnum = "DRESS" +) + +var AllImageTypeGroupEnum = []ImageTypeGroupEnum{ + ImageTypeGroupEnumShot, + ImageTypeGroupEnumCrop, + ImageTypeGroupEnumView, + ImageTypeGroupEnumPosture, + ImageTypeGroupEnumDress, +} + +func (e ImageTypeGroupEnum) IsValid() bool { + switch e { + case ImageTypeGroupEnumShot, ImageTypeGroupEnumCrop, ImageTypeGroupEnumView, ImageTypeGroupEnumPosture, ImageTypeGroupEnumDress: + return true + } + return false +} + +func (e ImageTypeGroupEnum) String() string { + return string(e) +} + +func (e *ImageTypeGroupEnum) UnmarshalGQL(v any) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = ImageTypeGroupEnum(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid ImageTypeGroupEnum", str) + } + return nil +} + +func (e ImageTypeGroupEnum) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} + +func (e *ImageTypeGroupEnum) UnmarshalJSON(b []byte) error { + s, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + return e.UnmarshalGQL(s) +} + +func (e ImageTypeGroupEnum) MarshalJSON() ([]byte, error) { + var buf bytes.Buffer + e.MarshalGQL(&buf) + return buf.Bytes(), nil +} + +// The kinds of entity an image type may be applied to. +// +// Every value seeded today is PERFORMER-only. When scenes and studios get +// image labelling, they get their own separate types and groups, not rows +// here with SCENE or STUDIO added to a type's `valid_types` +type ImageTypeScopeEnum string + +const ( + ImageTypeScopeEnumPerformer ImageTypeScopeEnum = "PERFORMER" + ImageTypeScopeEnumScene ImageTypeScopeEnum = "SCENE" + ImageTypeScopeEnumStudio ImageTypeScopeEnum = "STUDIO" +) + +var AllImageTypeScopeEnum = []ImageTypeScopeEnum{ + ImageTypeScopeEnumPerformer, + ImageTypeScopeEnumScene, + ImageTypeScopeEnumStudio, +} + +func (e ImageTypeScopeEnum) IsValid() bool { + switch e { + case ImageTypeScopeEnumPerformer, ImageTypeScopeEnumScene, ImageTypeScopeEnumStudio: + return true + } + return false +} + +func (e ImageTypeScopeEnum) String() string { + return string(e) +} + +func (e *ImageTypeScopeEnum) UnmarshalGQL(v any) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = ImageTypeScopeEnum(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid ImageTypeScopeEnum", str) + } + return nil +} + +func (e ImageTypeScopeEnum) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} + +func (e *ImageTypeScopeEnum) UnmarshalJSON(b []byte) error { + s, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + return e.UnmarshalGQL(s) +} + +func (e ImageTypeScopeEnum) MarshalJSON() ([]byte, error) { + var buf bytes.Buffer + e.MarshalGQL(&buf) + return buf.Bytes(), nil +} + type ModAuditActionEnum string const ( diff --git a/internal/models/model_edit.go b/internal/models/model_edit.go index 52904a20c..7c671b972 100644 --- a/internal/models/model_edit.go +++ b/internal/models/model_edit.go @@ -224,7 +224,17 @@ type PerformerEdit struct { RemovedPiercings []BodyModification `json:"removed_piercings,omitempty"` AddedImages []uuid.UUID `json:"added_images,omitempty"` RemovedImages []uuid.UUID `json:"removed_images,omitempty"` - DraftID *uuid.UUID `json:"draft_id,omitempty"` + // Type assignments travel in their own keys rather than as objects inside + // added_images/removed_images: those stay flat UUID arrays because + // FindUnusedImages parses them, and a parse that silently yields no rows + // would make the image GC delete images that pending edits still reference. + AddedImageTypes []ImageTypeAssignment `json:"added_image_types,omitempty"` + RemovedImageTypes []ImageTypeAssignment `json:"removed_image_types,omitempty"` + // Dates get their own key rather than riding inside the type tuples: a + // date belongs to the image-on-entity while the tuples belong to + // individual labels, and a date change with no label change is valid. + ImageDates []ImageDate `json:"image_dates,omitempty"` + DraftID *uuid.UUID `json:"draft_id,omitempty"` } func (PerformerEdit) IsEditDetails() {} diff --git a/internal/models/model_image_type.go b/internal/models/model_image_type.go new file mode 100644 index 000000000..f0ae1b135 --- /dev/null +++ b/internal/models/model_image_type.go @@ -0,0 +1,18 @@ +package models + +import "github.com/gofrs/uuid" + +// ImageTypeAssignment is one label applied to one image's presence on an +// entity. It is not exposed directly; TypedImage groups these by image. +type ImageTypeAssignment struct { + ImageID uuid.UUID `json:"image_id"` + Type ImageTypeEnum `json:"type"` +} + +// ImageDate is one image's date on an entity. Date is deliberately not +// omitempty: an explicit null is how an edit clears a date, and is different +// from the entry being absent, which leaves it alone. +type ImageDate struct { + ImageID uuid.UUID `json:"image_id"` + Date *string `json:"date"` +} diff --git a/internal/queries/copyfrom.go b/internal/queries/copyfrom.go index dcb571128..a0e305012 100644 --- a/internal/queries/copyfrom.go +++ b/internal/queries/copyfrom.go @@ -42,6 +42,41 @@ func (q *Queries) CreatePerformerAliases(ctx context.Context, arg []CreatePerfor return q.db.CopyFrom(ctx, []string{"performer_aliases"}, []string{"performer_id", "alias"}, &iteratorForCreatePerformerAliases{rows: arg}) } +// iteratorForCreatePerformerImageTypes implements pgx.CopyFromSource. +type iteratorForCreatePerformerImageTypes struct { + rows []CreatePerformerImageTypesParams + skippedFirstNextCall bool +} + +func (r *iteratorForCreatePerformerImageTypes) Next() bool { + if len(r.rows) == 0 { + return false + } + if !r.skippedFirstNextCall { + r.skippedFirstNextCall = true + return true + } + r.rows = r.rows[1:] + return len(r.rows) > 0 +} + +func (r iteratorForCreatePerformerImageTypes) Values() ([]interface{}, error) { + return []interface{}{ + r.rows[0].PerformerID, + r.rows[0].ImageID, + r.rows[0].TypeKey, + }, nil +} + +func (r iteratorForCreatePerformerImageTypes) Err() error { + return nil +} + +// Performer assignments +func (q *Queries) CreatePerformerImageTypes(ctx context.Context, arg []CreatePerformerImageTypesParams) (int64, error) { + return q.db.CopyFrom(ctx, []string{"performer_image_types"}, []string{"performer_id", "image_id", "type_key"}, &iteratorForCreatePerformerImageTypes{rows: arg}) +} + // iteratorForCreatePerformerImages implements pgx.CopyFromSource. type iteratorForCreatePerformerImages struct { rows []CreatePerformerImagesParams @@ -64,6 +99,7 @@ func (r iteratorForCreatePerformerImages) Values() ([]interface{}, error) { return []interface{}{ r.rows[0].PerformerID, r.rows[0].ImageID, + r.rows[0].Date, }, nil } @@ -72,7 +108,7 @@ func (r iteratorForCreatePerformerImages) Err() error { } func (q *Queries) CreatePerformerImages(ctx context.Context, arg []CreatePerformerImagesParams) (int64, error) { - return q.db.CopyFrom(ctx, []string{"performer_images"}, []string{"performer_id", "image_id"}, &iteratorForCreatePerformerImages{rows: arg}) + return q.db.CopyFrom(ctx, []string{"performer_images"}, []string{"performer_id", "image_id", "date"}, &iteratorForCreatePerformerImages{rows: arg}) } // iteratorForCreatePerformerPiercings implements pgx.CopyFromSource. @@ -486,6 +522,74 @@ func (q *Queries) CreateTagAliases(ctx context.Context, arg []CreateTagAliasesPa return q.db.CopyFrom(ctx, []string{"tag_aliases"}, []string{"tag_id", "alias"}, &iteratorForCreateTagAliases{rows: arg}) } +// iteratorForCreateUserImageTypeGroupPreferences implements pgx.CopyFromSource. +type iteratorForCreateUserImageTypeGroupPreferences struct { + rows []CreateUserImageTypeGroupPreferencesParams + skippedFirstNextCall bool +} + +func (r *iteratorForCreateUserImageTypeGroupPreferences) Next() bool { + if len(r.rows) == 0 { + return false + } + if !r.skippedFirstNextCall { + r.skippedFirstNextCall = true + return true + } + r.rows = r.rows[1:] + return len(r.rows) > 0 +} + +func (r iteratorForCreateUserImageTypeGroupPreferences) Values() ([]interface{}, error) { + return []interface{}{ + r.rows[0].UserID, + r.rows[0].GroupKey, + r.rows[0].SortOrder, + }, nil +} + +func (r iteratorForCreateUserImageTypeGroupPreferences) Err() error { + return nil +} + +func (q *Queries) CreateUserImageTypeGroupPreferences(ctx context.Context, arg []CreateUserImageTypeGroupPreferencesParams) (int64, error) { + return q.db.CopyFrom(ctx, []string{"user_image_type_group_preferences"}, []string{"user_id", "group_key", "sort_order"}, &iteratorForCreateUserImageTypeGroupPreferences{rows: arg}) +} + +// iteratorForCreateUserImageTypePreferences implements pgx.CopyFromSource. +type iteratorForCreateUserImageTypePreferences struct { + rows []CreateUserImageTypePreferencesParams + skippedFirstNextCall bool +} + +func (r *iteratorForCreateUserImageTypePreferences) Next() bool { + if len(r.rows) == 0 { + return false + } + if !r.skippedFirstNextCall { + r.skippedFirstNextCall = true + return true + } + r.rows = r.rows[1:] + return len(r.rows) > 0 +} + +func (r iteratorForCreateUserImageTypePreferences) Values() ([]interface{}, error) { + return []interface{}{ + r.rows[0].UserID, + r.rows[0].TypeKey, + r.rows[0].SortOrder, + }, nil +} + +func (r iteratorForCreateUserImageTypePreferences) Err() error { + return nil +} + +func (q *Queries) CreateUserImageTypePreferences(ctx context.Context, arg []CreateUserImageTypePreferencesParams) (int64, error) { + return q.db.CopyFrom(ctx, []string{"user_image_type_preferences"}, []string{"user_id", "type_key", "sort_order"}, &iteratorForCreateUserImageTypePreferences{rows: arg}) +} + // iteratorForCreateUserNotificationSubscriptions implements pgx.CopyFromSource. type iteratorForCreateUserNotificationSubscriptions struct { rows []CreateUserNotificationSubscriptionsParams diff --git a/internal/queries/edit.sql.go b/internal/queries/edit.sql.go index 5fd254895..15010cd4a 100644 --- a/internal/queries/edit.sql.go +++ b/internal/queries/edit.sql.go @@ -922,44 +922,163 @@ func (q *Queries) GetEditsByTag(ctx context.Context, tagID uuid.UUID) ([]Edit, e return items, nil } -const getImagesForEdit = `-- name: GetImagesForEdit :many +const getImageDatesForEdit = `-- name: GetImageDatesForEdit :many WITH edit AS ( SELECT id, user_id, operation, target_type, data, votes, status, applied, created_at, updated_at, closed_at, bot, update_count FROM edits WHERE edits.id = $1 -), current_images AS ( - SELECT si.image_id FROM edit e - JOIN scene_edits se ON e.id = se.edit_id - JOIN scene_images si ON se.scene_id = si.scene_id - UNION ALL - SELECT pi.image_id FROM edit e +), final_images AS ( + SELECT fi.image_id FROM edit_final_images fi WHERE fi.edit_id = $1 +), +current_dates AS ( + SELECT pi.image_id, pi.date FROM edit e JOIN performer_edits pe ON e.id = pe.edit_id JOIN performer_images pi ON pe.performer_id = pi.performer_id +), +changed_dates AS ( + -- An image named twice in one payload keeps the last entry. DISTINCT ON + -- without an ORDER BY leaves the winner up to the plan, so the ordinality + -- is carried through to say which one that is. + SELECT DISTINCT ON (image_id) + (elem->>'image_id')::uuid AS image_id, + elem->>'date' AS date + FROM edit, + jsonb_array_elements(COALESCE(data->'new_data'->'image_dates', '[]'::jsonb)) + WITH ORDINALITY AS entries(elem, position) + ORDER BY image_id, position DESC +), +final_dates AS ( + -- An override rather than a set union: an image has one date, so an entry + -- in image_dates replaces the current value, including replacing it with + -- null. Presence of the entry is what counts, not whether its value is + -- null, which is why the two branches split on existence. + SELECT fi.image_id, cur.date + FROM final_images fi + LEFT JOIN current_dates cur ON cur.image_id = fi.image_id + WHERE NOT EXISTS (SELECT 1 FROM changed_dates cd WHERE cd.image_id = fi.image_id) UNION ALL - SELECT sti.image_id FROM edit e - JOIN studio_edits ste ON e.id = ste.edit_id - JOIN studio_images sti ON ste.studio_id = sti.studio_id + SELECT fi.image_id, cd.date + FROM final_images fi + JOIN changed_dates cd ON cd.image_id = fi.image_id +) +SELECT image_id, date FROM final_dates ORDER BY image_id +` + +type GetImageDatesForEditRow struct { + ImageID uuid.UUID `db:"image_id" json:"image_id"` + Date *string `db:"date" json:"date"` +} + +// Gets current image dates for the target entity and applies the edit's +// image_dates. Not optional: updateImagesFromEdit deletes every join row and +// rebuilds it, and date is a column on those rows, so anything not +// written back here is lost on every applied edit -- including edits that +// never mention images. +// +// final_images comes from the edit_final_images view, which is the only +// statement of that chain. +// Only the performer branch carries a date in phase 1. Scene and studio join +// tables gain the column with their taxonomies. +func (q *Queries) GetImageDatesForEdit(ctx context.Context, id uuid.UUID) ([]GetImageDatesForEditRow, error) { + rows, err := q.db.Query(ctx, getImageDatesForEdit, id) + if err != nil { + return nil, err + } + defer rows.Close() + items := []GetImageDatesForEditRow{} + for rows.Next() { + var i GetImageDatesForEditRow + if err := rows.Scan(&i.ImageID, &i.Date); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getImageTypesForEdit = `-- name: GetImageTypesForEdit :many +WITH edit AS ( + SELECT id, user_id, operation, target_type, data, votes, status, applied, created_at, updated_at, closed_at, bot, update_count FROM edits WHERE edits.id = $1 +), final_images AS ( + SELECT fi.image_id FROM edit_final_images fi WHERE fi.edit_id = $1 ), -removed_images AS ( - SELECT jsonb_array_elements_text(COALESCE(data->'new_data'->'removed_images', '[]'::jsonb))::uuid AS image_id - FROM edit +current_assignments AS ( + SELECT pit.image_id, pit.type_key FROM edit e + JOIN performer_edits pe ON e.id = pe.edit_id + JOIN performer_image_types pit ON pe.performer_id = pit.performer_id ), -added_images AS ( - SELECT jsonb_array_elements_text(COALESCE(data->'new_data'->'added_images', '[]'::jsonb))::uuid AS image_id - FROM edit +removed_image_types AS ( + SELECT + (elem->>'image_id')::uuid AS image_id, + elem->>'type' AS type_key + FROM edit, jsonb_array_elements(COALESCE(data->'new_data'->'removed_image_types', '[]'::jsonb)) AS elem +), +added_image_types AS ( + SELECT + (elem->>'image_id')::uuid AS image_id, + elem->>'type' AS type_key + FROM edit, jsonb_array_elements(COALESCE(data->'new_data'->'added_image_types', '[]'::jsonb)) AS elem ), -final_images AS ( - SELECT image_id FROM current_images - WHERE image_id NOT IN (SELECT image_id FROM removed_images) +final_image_types AS ( + SELECT image_id, type_key FROM current_assignments + WHERE (image_id, type_key) NOT IN (SELECT image_id, type_key FROM removed_image_types) UNION - SELECT image_id FROM added_images + SELECT image_id, type_key FROM added_image_types ) -SELECT i.id, i.url, i.width, i.height, i.checksum FROM final_images fi +SELECT DISTINCT fit.image_id, fit.type_key FROM final_image_types fit +JOIN final_images fi ON fi.image_id = fit.image_id +ORDER BY fit.image_id, fit.type_key +` + +type GetImageTypesForEditRow struct { + ImageID uuid.UUID `db:"image_id" json:"image_id"` + TypeKey string `db:"type_key" json:"type_key"` +} + +// Gets current type assignments for the target entity and merges with the +// edit's added_image_types/removed_image_types. +// +// With edit/performer.go's diffImageTypes this implements the edit column of +// the table on ImageAssignmentInput in graphql/schema/types/image_type.graphql. +// The absent/null/empty distinctions are decided at submission and reach here +// only as which tuples the payload carries. +// Only the performer branch exists in phase 1. Scene and studio assignment +// tables arrive with their taxonomies, and become two more UNION ALL branches. +// Restricting to final_images is an invariant, not an optimisation: an edit +// that removes an image whose assignment survives in current_assignments would +// otherwise insert an assignment for an image the entity no longer has, and +// the composite foreign key would reject it. +func (q *Queries) GetImageTypesForEdit(ctx context.Context, id uuid.UUID) ([]GetImageTypesForEditRow, error) { + rows, err := q.db.Query(ctx, getImageTypesForEdit, id) + if err != nil { + return nil, err + } + defer rows.Close() + items := []GetImageTypesForEditRow{} + for rows.Next() { + var i GetImageTypesForEditRow + if err := rows.Scan(&i.ImageID, &i.TypeKey); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getImagesForEdit = `-- name: GetImagesForEdit :many +SELECT i.id, i.url, i.width, i.height, i.checksum FROM edit_final_images fi JOIN images i ON fi.image_id = i.id +WHERE fi.edit_id = $1 ORDER BY i.id ` // Gets current images for target entity and merges with edit's added_images/removed_images -func (q *Queries) GetImagesForEdit(ctx context.Context, id uuid.UUID) ([]Image, error) { - rows, err := q.db.Query(ctx, getImagesForEdit, id) +func (q *Queries) GetImagesForEdit(ctx context.Context, editID uuid.UUID) ([]Image, error) { + rows, err := q.db.Query(ctx, getImagesForEdit, editID) if err != nil { return nil, err } diff --git a/internal/queries/image.sql.go b/internal/queries/image.sql.go index 1ad07c9aa..01ebc3a43 100644 --- a/internal/queries/image.sql.go +++ b/internal/queries/image.sql.go @@ -95,15 +95,20 @@ FROM performer_images WHERE performer_images.performer_id = ANY($1::UUID[]) ` -func (q *Queries) FindImageIdsByPerformerIds(ctx context.Context, dollar_1 []uuid.UUID) ([]PerformerImage, error) { +type FindImageIdsByPerformerIdsRow struct { + PerformerID uuid.UUID `db:"performer_id" json:"performer_id"` + ImageID uuid.UUID `db:"image_id" json:"image_id"` +} + +func (q *Queries) FindImageIdsByPerformerIds(ctx context.Context, dollar_1 []uuid.UUID) ([]FindImageIdsByPerformerIdsRow, error) { rows, err := q.db.Query(ctx, findImageIdsByPerformerIds, dollar_1) if err != nil { return nil, err } defer rows.Close() - items := []PerformerImage{} + items := []FindImageIdsByPerformerIdsRow{} for rows.Next() { - var i PerformerImage + var i FindImageIdsByPerformerIdsRow if err := rows.Scan(&i.PerformerID, &i.ImageID); err != nil { return nil, err } @@ -285,6 +290,10 @@ AND drafts.id IS NULL LIMIT 1000 ` +// The added_images path below has no COALESCE, and does not need one: a +// pending edit predating image types has no such key, and jsonb_array_elements +// is STRICT, so a set-returning function given NULL yields zero rows rather +// than erroring. Keep added_images a flat UUID array for the same reason. func (q *Queries) FindUnusedImages(ctx context.Context) ([]Image, error) { rows, err := q.db.Query(ctx, findUnusedImages) if err != nil { diff --git a/internal/queries/image_type.sql.go b/internal/queries/image_type.sql.go new file mode 100644 index 000000000..1ef892916 --- /dev/null +++ b/internal/queries/image_type.sql.go @@ -0,0 +1,351 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.29.0 +// source: image_type.sql + +package queries + +import ( + "context" + + "github.com/gofrs/uuid" +) + +type CreatePerformerImageTypesParams struct { + PerformerID uuid.UUID `db:"performer_id" json:"performer_id"` + ImageID uuid.UUID `db:"image_id" json:"image_id"` + TypeKey string `db:"type_key" json:"type_key"` +} + +type CreateUserImageTypeGroupPreferencesParams struct { + UserID uuid.UUID `db:"user_id" json:"user_id"` + GroupKey string `db:"group_key" json:"group_key"` + SortOrder int `db:"sort_order" json:"sort_order"` +} + +type CreateUserImageTypePreferencesParams struct { + UserID uuid.UUID `db:"user_id" json:"user_id"` + TypeKey string `db:"type_key" json:"type_key"` + SortOrder int `db:"sort_order" json:"sort_order"` +} + +const deletePerformerImageTypes = `-- name: DeletePerformerImageTypes :exec +DELETE FROM performer_image_types WHERE performer_id = $1 +` + +func (q *Queries) DeletePerformerImageTypes(ctx context.Context, performerID uuid.UUID) error { + _, err := q.db.Exec(ctx, deletePerformerImageTypes, performerID) + return err +} + +const deleteUserImageTypeGroupPreferences = `-- name: DeleteUserImageTypeGroupPreferences :exec +DELETE FROM user_image_type_group_preferences WHERE user_id = $1 +` + +func (q *Queries) DeleteUserImageTypeGroupPreferences(ctx context.Context, userID uuid.UUID) error { + _, err := q.db.Exec(ctx, deleteUserImageTypeGroupPreferences, userID) + return err +} + +const deleteUserImageTypePreferences = `-- name: DeleteUserImageTypePreferences :exec +DELETE FROM user_image_type_preferences WHERE user_id = $1 +` + +func (q *Queries) DeleteUserImageTypePreferences(ctx context.Context, userID uuid.UUID) error { + _, err := q.db.Exec(ctx, deleteUserImageTypePreferences, userID) + return err +} + +const findImageDatesByPerformerIds = `-- name: FindImageDatesByPerformerIds :many +SELECT performer_id, image_id, date +FROM performer_images +WHERE performer_id = ANY($1::UUID[]) +` + +func (q *Queries) FindImageDatesByPerformerIds(ctx context.Context, performerIds []uuid.UUID) ([]PerformerImage, error) { + rows, err := q.db.Query(ctx, findImageDatesByPerformerIds, performerIds) + if err != nil { + return nil, err + } + defer rows.Close() + items := []PerformerImage{} + for rows.Next() { + var i PerformerImage + if err := rows.Scan(&i.PerformerID, &i.ImageID, &i.Date); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const findImageTypesByPerformerIds = `-- name: FindImageTypesByPerformerIds :many +SELECT pit.performer_id, pit.image_id, pit.type_key +FROM performer_image_types pit +JOIN image_types it ON it.key = pit.type_key +JOIN image_type_groups itg ON itg.key = it.group_key +WHERE pit.performer_id = ANY($1::UUID[]) +ORDER BY itg.sort_order ASC, it.sort_order ASC +` + +// Ordered by the vocabulary rather than alphabetically, so an image's labels +// read in the same priority order the admin set. +func (q *Queries) FindImageTypesByPerformerIds(ctx context.Context, performerIds []uuid.UUID) ([]PerformerImageType, error) { + rows, err := q.db.Query(ctx, findImageTypesByPerformerIds, performerIds) + if err != nil { + return nil, err + } + defer rows.Close() + items := []PerformerImageType{} + for rows.Next() { + var i PerformerImageType + if err := rows.Scan(&i.PerformerID, &i.ImageID, &i.TypeKey); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getAllImageTypeConflicts = `-- name: GetAllImageTypeConflicts :many +SELECT type_key, conflicts_with_key FROM image_type_conflicts +` + +func (q *Queries) GetAllImageTypeConflicts(ctx context.Context) ([]ImageTypeConflict, error) { + rows, err := q.db.Query(ctx, getAllImageTypeConflicts) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ImageTypeConflict{} + for rows.Next() { + var i ImageTypeConflict + if err := rows.Scan(&i.TypeKey, &i.ConflictsWithKey); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getAllImageTypeGroups = `-- name: GetAllImageTypeGroups :many + +SELECT key, name, description, sort_order, exclusive, enabled FROM image_type_groups ORDER BY sort_order ASC +` + +// Image type vocabulary queries. +// +// The vocabulary is seeded by migration and only sort_order is writable at +// runtime, so there is no create or delete here. +func (q *Queries) GetAllImageTypeGroups(ctx context.Context) ([]ImageTypeGroup, error) { + rows, err := q.db.Query(ctx, getAllImageTypeGroups) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ImageTypeGroup{} + for rows.Next() { + var i ImageTypeGroup + if err := rows.Scan( + &i.Key, + &i.Name, + &i.Description, + &i.SortOrder, + &i.Exclusive, + &i.Enabled, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getAllImageTypes = `-- name: GetAllImageTypes :many +SELECT key, name, description, group_key, sort_order, valid_types, enabled FROM image_types ORDER BY group_key ASC, sort_order ASC +` + +func (q *Queries) GetAllImageTypes(ctx context.Context) ([]ImageType, error) { + rows, err := q.db.Query(ctx, getAllImageTypes) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ImageType{} + for rows.Next() { + var i ImageType + if err := rows.Scan( + &i.Key, + &i.Name, + &i.Description, + &i.GroupKey, + &i.SortOrder, + &i.ValidTypes, + &i.Enabled, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getImageTypesByTarget = `-- name: GetImageTypesByTarget :many +SELECT key, name, description, group_key, sort_order, valid_types, enabled FROM image_types +WHERE $1::text = ANY(valid_types) +ORDER BY group_key ASC, sort_order ASC +` + +// Types valid for one entity kind. $1 is a bare target name, e.g. 'PERFORMER'. +func (q *Queries) GetImageTypesByTarget(ctx context.Context, target string) ([]ImageType, error) { + rows, err := q.db.Query(ctx, getImageTypesByTarget, target) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ImageType{} + for rows.Next() { + var i ImageType + if err := rows.Scan( + &i.Key, + &i.Name, + &i.Description, + &i.GroupKey, + &i.SortOrder, + &i.ValidTypes, + &i.Enabled, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getUserImageTypeGroupPreferences = `-- name: GetUserImageTypeGroupPreferences :many +SELECT group_key FROM user_image_type_group_preferences +WHERE user_id = $1 +ORDER BY sort_order ASC +` + +func (q *Queries) GetUserImageTypeGroupPreferences(ctx context.Context, userID uuid.UUID) ([]string, error) { + rows, err := q.db.Query(ctx, getUserImageTypeGroupPreferences, userID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []string{} + for rows.Next() { + var group_key string + if err := rows.Scan(&group_key); err != nil { + return nil, err + } + items = append(items, group_key) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getUserImageTypePreferences = `-- name: GetUserImageTypePreferences :many + +SELECT type_key FROM user_image_type_preferences +WHERE user_id = $1 +ORDER BY sort_order ASC +` + +// User preferences +func (q *Queries) GetUserImageTypePreferences(ctx context.Context, userID uuid.UUID) ([]string, error) { + rows, err := q.db.Query(ctx, getUserImageTypePreferences, userID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []string{} + for rows.Next() { + var type_key string + if err := rows.Scan(&type_key); err != nil { + return nil, err + } + items = append(items, type_key) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const setImageTypeGroupsEnabled = `-- name: SetImageTypeGroupsEnabled :exec + +UPDATE image_type_groups SET enabled = NOT ("key" = ANY(COALESCE($1::text[], '{}'))) +` + +// Enabling +// Takes the complete set of disabled keys, so a group absent from the list is +// enabled. A type added to the vocabulary later is therefore on by default, +// which an "enabled set" would have silently reversed. +// +// COALESCE because an array arriving as SQL NULL would set enabled = NULL on +// every row rather than enabling them: NOT (key = ANY(NULL)) is NULL, not true. +func (q *Queries) SetImageTypeGroupsEnabled(ctx context.Context, disabled []string) error { + _, err := q.db.Exec(ctx, setImageTypeGroupsEnabled, disabled) + return err +} + +const setImageTypesEnabled = `-- name: SetImageTypesEnabled :exec +UPDATE image_types SET enabled = NOT ("key" = ANY(COALESCE($1::text[], '{}'))) +` + +func (q *Queries) SetImageTypesEnabled(ctx context.Context, disabled []string) error { + _, err := q.db.Exec(ctx, setImageTypesEnabled, disabled) + return err +} + +const updateImageTypeGroupSortOrder = `-- name: UpdateImageTypeGroupSortOrder :exec +UPDATE image_type_groups SET sort_order = $2 WHERE key = $1 +` + +type UpdateImageTypeGroupSortOrderParams struct { + Key string `db:"key" json:"key"` + SortOrder int `db:"sort_order" json:"sort_order"` +} + +// Both sort_order unique constraints are deferred, so a reorder can be one +// UPDATE per row without contriving a collision-free intermediate permutation. +func (q *Queries) UpdateImageTypeGroupSortOrder(ctx context.Context, arg UpdateImageTypeGroupSortOrderParams) error { + _, err := q.db.Exec(ctx, updateImageTypeGroupSortOrder, arg.Key, arg.SortOrder) + return err +} + +const updateImageTypeSortOrder = `-- name: UpdateImageTypeSortOrder :exec +UPDATE image_types SET sort_order = $2 WHERE key = $1 +` + +type UpdateImageTypeSortOrderParams struct { + Key string `db:"key" json:"key"` + SortOrder int `db:"sort_order" json:"sort_order"` +} + +func (q *Queries) UpdateImageTypeSortOrder(ctx context.Context, arg UpdateImageTypeSortOrderParams) error { + _, err := q.db.Exec(ctx, updateImageTypeSortOrder, arg.Key, arg.SortOrder) + return err +} diff --git a/internal/queries/models.go b/internal/queries/models.go index 2f216daa4..554e1b1c7 100644 --- a/internal/queries/models.go +++ b/internal/queries/models.go @@ -144,6 +144,11 @@ type EditComment struct { IsHidden bool `db:"is_hidden" json:"is_hidden"` } +type EditFinalImage struct { + EditID uuid.UUID `db:"edit_id" json:"edit_id"` + ImageID uuid.UUID `db:"image_id" json:"image_id"` +} + type EditVote struct { EditID uuid.UUID `db:"edit_id" json:"edit_id"` UserID uuid.NullUUID `db:"user_id" json:"user_id"` @@ -165,6 +170,30 @@ type Image struct { Checksum string `db:"checksum" json:"checksum"` } +type ImageType struct { + Key string `db:"key" json:"key"` + Name string `db:"name" json:"name"` + Description *string `db:"description" json:"description"` + GroupKey string `db:"group_key" json:"group_key"` + SortOrder int `db:"sort_order" json:"sort_order"` + ValidTypes []string `db:"valid_types" json:"valid_types"` + Enabled bool `db:"enabled" json:"enabled"` +} + +type ImageTypeConflict struct { + TypeKey string `db:"type_key" json:"type_key"` + ConflictsWithKey string `db:"conflicts_with_key" json:"conflicts_with_key"` +} + +type ImageTypeGroup struct { + Key string `db:"key" json:"key"` + Name string `db:"name" json:"name"` + Description *string `db:"description" json:"description"` + SortOrder int `db:"sort_order" json:"sort_order"` + Exclusive bool `db:"exclusive" json:"exclusive"` + Enabled bool `db:"enabled" json:"enabled"` +} + type InviteKey struct { ID uuid.UUID `db:"id" json:"id"` GeneratedBy uuid.UUID `db:"generated_by" json:"generated_by"` @@ -236,6 +265,13 @@ type PerformerFavorite struct { type PerformerImage struct { PerformerID uuid.UUID `db:"performer_id" json:"performer_id"` ImageID uuid.UUID `db:"image_id" json:"image_id"` + Date *string `db:"date" json:"date"` +} + +type PerformerImageType struct { + PerformerID uuid.UUID `db:"performer_id" json:"performer_id"` + ImageID uuid.UUID `db:"image_id" json:"image_id"` + TypeKey string `db:"type_key" json:"type_key"` } type PerformerPiercing struct { @@ -476,6 +512,18 @@ type User struct { InviteTokens int `db:"invite_tokens" json:"invite_tokens"` } +type UserImageTypeGroupPreference struct { + UserID uuid.UUID `db:"user_id" json:"user_id"` + GroupKey string `db:"group_key" json:"group_key"` + SortOrder int `db:"sort_order" json:"sort_order"` +} + +type UserImageTypePreference struct { + UserID uuid.UUID `db:"user_id" json:"user_id"` + TypeKey string `db:"type_key" json:"type_key"` + SortOrder int `db:"sort_order" json:"sort_order"` +} + type UserNotification struct { UserID uuid.UUID `db:"user_id" json:"user_id"` Type NotificationType `db:"type" json:"type"` diff --git a/internal/queries/performer.sql.go b/internal/queries/performer.sql.go index c06ec572f..4f136769f 100644 --- a/internal/queries/performer.sql.go +++ b/internal/queries/performer.sql.go @@ -171,6 +171,7 @@ func (q *Queries) CreatePerformerFavorite(ctx context.Context, arg CreatePerform type CreatePerformerImagesParams struct { PerformerID uuid.UUID `db:"performer_id" json:"performer_id"` ImageID uuid.UUID `db:"image_id" json:"image_id"` + Date *string `db:"date" json:"date"` } type CreatePerformerPiercingsParams struct { diff --git a/internal/queries/querier.go b/internal/queries/querier.go index aff8cff88..daf54da38 100644 --- a/internal/queries/querier.go +++ b/internal/queries/querier.go @@ -42,6 +42,8 @@ type Querier interface { CreatePerformerAliases(ctx context.Context, arg []CreatePerformerAliasesParams) (int64, error) CreatePerformerEdit(ctx context.Context, arg CreatePerformerEditParams) error CreatePerformerFavorite(ctx context.Context, arg CreatePerformerFavoriteParams) error + // Performer assignments + CreatePerformerImageTypes(ctx context.Context, arg []CreatePerformerImageTypesParams) (int64, error) CreatePerformerImages(ctx context.Context, arg []CreatePerformerImagesParams) (int64, error) CreatePerformerPiercings(ctx context.Context, arg []CreatePerformerPiercingsParams) (int64, error) // Performer redirects @@ -89,6 +91,8 @@ type Querier interface { CreateTagRedirect(ctx context.Context, arg CreateTagRedirectParams) error // User queries CreateUser(ctx context.Context, arg CreateUserParams) (User, error) + CreateUserImageTypeGroupPreferences(ctx context.Context, arg []CreateUserImageTypeGroupPreferencesParams) (int64, error) + CreateUserImageTypePreferences(ctx context.Context, arg []CreateUserImageTypePreferencesParams) (int64, error) // User notification subscriptions CreateUserNotificationSubscriptions(ctx context.Context, arg []CreateUserNotificationSubscriptionsParams) (int64, error) // User roles @@ -111,6 +115,7 @@ type Querier interface { DeletePerformerFavorite(ctx context.Context, arg DeletePerformerFavoriteParams) error // Performer favorites DeletePerformerFavorites(ctx context.Context, performerID uuid.UUID) error + DeletePerformerImageTypes(ctx context.Context, performerID uuid.UUID) error DeletePerformerImages(ctx context.Context, performerID uuid.UUID) error // Performer piercings DeletePerformerPiercings(ctx context.Context, performerID uuid.UUID) error @@ -142,6 +147,8 @@ type Querier interface { DeleteTagAliasesByNames(ctx context.Context, arg DeleteTagAliasesByNamesParams) error DeleteTagCategory(ctx context.Context, id uuid.UUID) error DeleteUser(ctx context.Context, id uuid.UUID) error + DeleteUserImageTypeGroupPreferences(ctx context.Context, userID uuid.UUID) error + DeleteUserImageTypePreferences(ctx context.Context, userID uuid.UUID) error DeleteUserNotificationSubscriptions(ctx context.Context, userID uuid.UUID) error DeleteUserRoles(ctx context.Context, userID uuid.UUID) error DeleteUserToken(ctx context.Context, id uuid.UUID) error @@ -166,9 +173,13 @@ type Querier interface { FindExistingScenes(ctx context.Context, arg FindExistingScenesParams) ([]Scene, error) FindImage(ctx context.Context, id uuid.UUID) (Image, error) FindImageByChecksum(ctx context.Context, checksum string) (Image, error) - FindImageIdsByPerformerIds(ctx context.Context, dollar_1 []uuid.UUID) ([]PerformerImage, error) + FindImageDatesByPerformerIds(ctx context.Context, performerIds []uuid.UUID) ([]PerformerImage, error) + FindImageIdsByPerformerIds(ctx context.Context, dollar_1 []uuid.UUID) ([]FindImageIdsByPerformerIdsRow, error) FindImageIdsBySceneIds(ctx context.Context, dollar_1 []uuid.UUID) ([]SceneImage, error) FindImageIdsByStudioIds(ctx context.Context, dollar_1 []uuid.UUID) ([]StudioImage, error) + // Ordered by the vocabulary rather than alphabetically, so an image's labels + // read in the same priority order the admin set. + FindImageTypesByPerformerIds(ctx context.Context, performerIds []uuid.UUID) ([]PerformerImageType, error) FindImagesByIds(ctx context.Context, dollar_1 []uuid.UUID) ([]Image, error) FindImagesBySceneID(ctx context.Context, id uuid.UUID) ([]Image, error) FindImagesByStudioID(ctx context.Context, id uuid.UUID) ([]Image, error) @@ -231,6 +242,10 @@ type Querier interface { FindTagsByIds(ctx context.Context, dollar_1 []uuid.UUID) ([]Tag, error) FindTagsBySceneID(ctx context.Context, sceneID uuid.UUID) ([]Tag, error) FindUnreadNotificationsByUser(ctx context.Context, arg FindUnreadNotificationsByUserParams) ([]Notification, error) + // The added_images path below has no COALESCE, and does not need one: a + // pending edit predating image types has no such key, and jsonb_array_elements + // is STRICT, so a set-returning function given NULL yields zero rows rather + // than erroring. Keep added_images a flat UUID array for the same reason. FindUnusedImages(ctx context.Context) ([]Image, error) FindUser(ctx context.Context, id uuid.UUID) (User, error) FindUserByEmail(ctx context.Context, upper interface{}) (User, error) @@ -242,6 +257,13 @@ type Querier interface { // Get all fingerprints for multiple scenes with aggregated vote data // When onlySubmitted is true, pass the actual user ID, when false pass NULL GetAllFingerprints(ctx context.Context, arg GetAllFingerprintsParams) ([]GetAllFingerprintsRow, error) + GetAllImageTypeConflicts(ctx context.Context) ([]ImageTypeConflict, error) + // Image type vocabulary queries. + // + // The vocabulary is seeded by migration and only sort_order is writable at + // runtime, so there is no create or delete here. + GetAllImageTypeGroups(ctx context.Context) ([]ImageTypeGroup, error) + GetAllImageTypes(ctx context.Context) ([]ImageType, error) GetAllSceneFingerprints(ctx context.Context, sceneID uuid.UUID) ([]GetAllSceneFingerprintsRow, error) GetAllSiteCategories(ctx context.Context) ([]SiteCategory, error) GetAllTagCategories(ctx context.Context) ([]TagCategory, error) @@ -261,8 +283,35 @@ type Querier interface { GetEditsByStudio(ctx context.Context, studioID uuid.UUID) ([]Edit, error) GetEditsByTag(ctx context.Context, tagID uuid.UUID) ([]Edit, error) GetFingerprint(ctx context.Context, arg GetFingerprintParams) (Fingerprint, error) + // Gets current image dates for the target entity and applies the edit's + // image_dates. Not optional: updateImagesFromEdit deletes every join row and + // rebuilds it, and date is a column on those rows, so anything not + // written back here is lost on every applied edit -- including edits that + // never mention images. + // + // final_images comes from the edit_final_images view, which is the only + // statement of that chain. + // Only the performer branch carries a date in phase 1. Scene and studio join + // tables gain the column with their taxonomies. + GetImageDatesForEdit(ctx context.Context, id uuid.UUID) ([]GetImageDatesForEditRow, error) + // Types valid for one entity kind. $1 is a bare target name, e.g. 'PERFORMER'. + GetImageTypesByTarget(ctx context.Context, target string) ([]ImageType, error) + // Gets current type assignments for the target entity and merges with the + // edit's added_image_types/removed_image_types. + // + // With edit/performer.go's diffImageTypes this implements the edit column of + // the table on ImageAssignmentInput in graphql/schema/types/image_type.graphql. + // The absent/null/empty distinctions are decided at submission and reach here + // only as which tuples the payload carries. + // Only the performer branch exists in phase 1. Scene and studio assignment + // tables arrive with their taxonomies, and become two more UNION ALL branches. + // Restricting to final_images is an invariant, not an optimisation: an edit + // that removes an image whose assignment survives in current_assignments would + // otherwise insert an assignment for an image the entity no longer has, and + // the composite foreign key would reject it. + GetImageTypesForEdit(ctx context.Context, id uuid.UUID) ([]GetImageTypesForEditRow, error) // Gets current images for target entity and merges with edit's added_images/removed_images - GetImagesForEdit(ctx context.Context, id uuid.UUID) ([]Image, error) + GetImagesForEdit(ctx context.Context, editID uuid.UUID) ([]Image, error) // Gets current performers for target entity and merges with edit's added_performers/removed_performers GetMergedPerformersForEdit(ctx context.Context, id uuid.UUID) ([]GetMergedPerformersForEditRow, error) // Gets current aliases for target studio entity and merges with edit's added_aliases/removed_aliases @@ -301,6 +350,9 @@ type Querier interface { GetStudiosByPerformerAndNetwork(ctx context.Context, arg GetStudiosByPerformerAndNetworkParams) ([]GetStudiosByPerformerAndNetworkRow, error) GetTagAliases(ctx context.Context, tagID uuid.UUID) ([]string, error) GetTagCategoriesByIds(ctx context.Context, dollar_1 []uuid.UUID) ([]TagCategory, error) + GetUserImageTypeGroupPreferences(ctx context.Context, userID uuid.UUID) ([]string, error) + // User preferences + GetUserImageTypePreferences(ctx context.Context, userID uuid.UUID) ([]string, error) GetUserNotificationSubscriptions(ctx context.Context, userID uuid.UUID) ([]NotificationType, error) GetUserRoles(ctx context.Context, userID uuid.UUID) ([]string, error) GetUsers(ctx context.Context, dollar_1 []uuid.UUID) ([]User, error) @@ -344,6 +396,15 @@ type Querier interface { SearchStudios(ctx context.Context, arg SearchStudiosParams) ([]SearchStudiosRow, error) SearchTags(ctx context.Context, arg SearchTagsParams) ([]Tag, error) SetEditCommentHidden(ctx context.Context, arg SetEditCommentHiddenParams) (EditComment, error) + // Enabling + // Takes the complete set of disabled keys, so a group absent from the list is + // enabled. A type added to the vocabulary later is therefore on by default, + // which an "enabled set" would have silently reversed. + // + // COALESCE because an array arriving as SQL NULL would set enabled = NULL on + // every row rather than enabling them: NOT (key = ANY(NULL)) is NULL, not true. + SetImageTypeGroupsEnabled(ctx context.Context, disabled []string) error + SetImageTypesEnabled(ctx context.Context, disabled []string) error SetScenePerformerAlias(ctx context.Context, arg SetScenePerformerAliasParams) error SoftDeletePerformer(ctx context.Context, id uuid.UUID) (Performer, error) SoftDeleteScene(ctx context.Context, id uuid.UUID) (Scene, error) @@ -362,6 +423,10 @@ type Querier interface { UpdateEdit(ctx context.Context, arg UpdateEditParams) (Edit, error) UpdateEditCommentText(ctx context.Context, arg UpdateEditCommentTextParams) (EditComment, error) UpdateEditData(ctx context.Context, arg UpdateEditDataParams) (Edit, error) + // Both sort_order unique constraints are deferred, so a reorder can be one + // UPDATE per row without contriving a collision-free intermediate permutation. + UpdateImageTypeGroupSortOrder(ctx context.Context, arg UpdateImageTypeGroupSortOrderParams) error + UpdateImageTypeSortOrder(ctx context.Context, arg UpdateImageTypeSortOrderParams) error UpdatePerformer(ctx context.Context, arg UpdatePerformerParams) (Performer, error) UpdatePerformerRedirects(ctx context.Context, arg UpdatePerformerRedirectsParams) error UpdateScene(ctx context.Context, arg UpdateSceneParams) (Scene, error) diff --git a/internal/queries/sql/edit.sql b/internal/queries/sql/edit.sql index 3979a45ba..0dc9b7c7f 100644 --- a/internal/queries/sql/edit.sql +++ b/internal/queries/sql/edit.sql @@ -169,38 +169,105 @@ ORDER BY url; -- name: GetImagesForEdit :many -- Gets current images for target entity and merges with edit's added_images/removed_images +SELECT i.* FROM edit_final_images fi +JOIN images i ON fi.image_id = i.id +WHERE fi.edit_id = $1 +ORDER BY i.id; + +-- name: GetImageTypesForEdit :many +-- Gets current type assignments for the target entity and merges with the +-- edit's added_image_types/removed_image_types. +-- +-- With edit/performer.go's diffImageTypes this implements the edit column of +-- the table on ImageAssignmentInput in graphql/schema/types/image_type.graphql. +-- The absent/null/empty distinctions are decided at submission and reach here +-- only as which tuples the payload carries. WITH edit AS ( SELECT * FROM edits WHERE edits.id = $1 -), current_images AS ( - SELECT si.image_id FROM edit e - JOIN scene_edits se ON e.id = se.edit_id - JOIN scene_images si ON se.scene_id = si.scene_id - UNION ALL - SELECT pi.image_id FROM edit e +), final_images AS ( + SELECT fi.image_id FROM edit_final_images fi WHERE fi.edit_id = $1 +), +-- Only the performer branch exists in phase 1. Scene and studio assignment +-- tables arrive with their taxonomies, and become two more UNION ALL branches. +current_assignments AS ( + SELECT pit.image_id, pit.type_key FROM edit e JOIN performer_edits pe ON e.id = pe.edit_id - JOIN performer_images pi ON pe.performer_id = pi.performer_id - UNION ALL - SELECT sti.image_id FROM edit e - JOIN studio_edits ste ON e.id = ste.edit_id - JOIN studio_images sti ON ste.studio_id = sti.studio_id + JOIN performer_image_types pit ON pe.performer_id = pit.performer_id ), -removed_images AS ( - SELECT jsonb_array_elements_text(COALESCE(data->'new_data'->'removed_images', '[]'::jsonb))::uuid AS image_id - FROM edit +removed_image_types AS ( + SELECT + (elem->>'image_id')::uuid AS image_id, + elem->>'type' AS type_key + FROM edit, jsonb_array_elements(COALESCE(data->'new_data'->'removed_image_types', '[]'::jsonb)) AS elem ), -added_images AS ( - SELECT jsonb_array_elements_text(COALESCE(data->'new_data'->'added_images', '[]'::jsonb))::uuid AS image_id - FROM edit +added_image_types AS ( + SELECT + (elem->>'image_id')::uuid AS image_id, + elem->>'type' AS type_key + FROM edit, jsonb_array_elements(COALESCE(data->'new_data'->'added_image_types', '[]'::jsonb)) AS elem ), -final_images AS ( - SELECT image_id FROM current_images - WHERE image_id NOT IN (SELECT image_id FROM removed_images) +final_image_types AS ( + SELECT image_id, type_key FROM current_assignments + WHERE (image_id, type_key) NOT IN (SELECT image_id, type_key FROM removed_image_types) UNION - SELECT image_id FROM added_images + SELECT image_id, type_key FROM added_image_types ) -SELECT i.* FROM final_images fi -JOIN images i ON fi.image_id = i.id -ORDER BY i.id; +-- Restricting to final_images is an invariant, not an optimisation: an edit +-- that removes an image whose assignment survives in current_assignments would +-- otherwise insert an assignment for an image the entity no longer has, and +-- the composite foreign key would reject it. +SELECT DISTINCT fit.image_id, fit.type_key FROM final_image_types fit +JOIN final_images fi ON fi.image_id = fit.image_id +ORDER BY fit.image_id, fit.type_key; + +-- name: GetImageDatesForEdit :many +-- Gets current image dates for the target entity and applies the edit's +-- image_dates. Not optional: updateImagesFromEdit deletes every join row and +-- rebuilds it, and date is a column on those rows, so anything not +-- written back here is lost on every applied edit -- including edits that +-- never mention images. +-- +-- final_images comes from the edit_final_images view, which is the only +-- statement of that chain. +WITH edit AS ( + SELECT * FROM edits WHERE edits.id = $1 +), final_images AS ( + SELECT fi.image_id FROM edit_final_images fi WHERE fi.edit_id = $1 +), +-- Only the performer branch carries a date in phase 1. Scene and studio join +-- tables gain the column with their taxonomies. +current_dates AS ( + SELECT pi.image_id, pi.date FROM edit e + JOIN performer_edits pe ON e.id = pe.edit_id + JOIN performer_images pi ON pe.performer_id = pi.performer_id +), +changed_dates AS ( + -- An image named twice in one payload keeps the last entry. DISTINCT ON + -- without an ORDER BY leaves the winner up to the plan, so the ordinality + -- is carried through to say which one that is. + SELECT DISTINCT ON (image_id) + (elem->>'image_id')::uuid AS image_id, + elem->>'date' AS date + FROM edit, + jsonb_array_elements(COALESCE(data->'new_data'->'image_dates', '[]'::jsonb)) + WITH ORDINALITY AS entries(elem, position) + ORDER BY image_id, position DESC +), +final_dates AS ( + -- An override rather than a set union: an image has one date, so an entry + -- in image_dates replaces the current value, including replacing it with + -- null. Presence of the entry is what counts, not whether its value is + -- null, which is why the two branches split on existence. + SELECT fi.image_id, cur.date + FROM final_images fi + LEFT JOIN current_dates cur ON cur.image_id = fi.image_id + WHERE NOT EXISTS (SELECT 1 FROM changed_dates cd WHERE cd.image_id = fi.image_id) + UNION ALL + SELECT fi.image_id, cd.date + FROM final_images fi + JOIN changed_dates cd ON cd.image_id = fi.image_id +) +SELECT image_id, date FROM final_dates ORDER BY image_id; -- name: GetEditTargetID :one SELECT CASE e.target_type diff --git a/internal/queries/sql/image.sql b/internal/queries/sql/image.sql index 1c728fd83..92413495c 100644 --- a/internal/queries/sql/image.sql +++ b/internal/queries/sql/image.sql @@ -30,6 +30,10 @@ WHERE studios.id = $1; SELECT * FROM images WHERE id = ANY($1::UUID[]); -- name: FindUnusedImages :many +-- The added_images path below has no COALESCE, and does not need one: a +-- pending edit predating image types has no such key, and jsonb_array_elements +-- is STRICT, so a set-returning function given NULL yields zero rows rather +-- than erroring. Keep added_images a flat UUID array for the same reason. SELECT images.* from images LEFT JOIN scene_images ON scene_images.image_id = images.id LEFT JOIN performer_images ON performer_images.image_id = images.id diff --git a/internal/queries/sql/image_type.sql b/internal/queries/sql/image_type.sql new file mode 100644 index 000000000..3bd4b7ca9 --- /dev/null +++ b/internal/queries/sql/image_type.sql @@ -0,0 +1,88 @@ +-- Image type vocabulary queries. +-- +-- The vocabulary is seeded by migration and only sort_order is writable at +-- runtime, so there is no create or delete here. + +-- name: GetAllImageTypeGroups :many +SELECT * FROM image_type_groups ORDER BY sort_order ASC; + +-- name: GetAllImageTypes :many +SELECT * FROM image_types ORDER BY group_key ASC, sort_order ASC; + +-- name: UpdateImageTypeGroupSortOrder :exec +-- Both sort_order unique constraints are deferred, so a reorder can be one +-- UPDATE per row without contriving a collision-free intermediate permutation. +UPDATE image_type_groups SET sort_order = $2 WHERE key = $1; + +-- name: UpdateImageTypeSortOrder :exec +UPDATE image_types SET sort_order = $2 WHERE key = $1; + +-- Performer assignments + +-- name: CreatePerformerImageTypes :copyfrom +INSERT INTO performer_image_types (performer_id, image_id, type_key) VALUES ($1, $2, $3); + +-- name: FindImageDatesByPerformerIds :many +SELECT performer_id, image_id, date +FROM performer_images +WHERE performer_id = ANY(sqlc.arg(performer_ids)::UUID[]); + +-- name: DeletePerformerImageTypes :exec +DELETE FROM performer_image_types WHERE performer_id = $1; + +-- name: FindImageTypesByPerformerIds :many +-- Ordered by the vocabulary rather than alphabetically, so an image's labels +-- read in the same priority order the admin set. +SELECT pit.performer_id, pit.image_id, pit.type_key +FROM performer_image_types pit +JOIN image_types it ON it.key = pit.type_key +JOIN image_type_groups itg ON itg.key = it.group_key +WHERE pit.performer_id = ANY(sqlc.arg(performer_ids)::UUID[]) +ORDER BY itg.sort_order ASC, it.sort_order ASC; + +-- name: GetImageTypesByTarget :many +-- Types valid for one entity kind. $1 is a bare target name, e.g. 'PERFORMER'. +SELECT * FROM image_types +WHERE sqlc.arg(target)::text = ANY(valid_types) +ORDER BY group_key ASC, sort_order ASC; + +-- User preferences + +-- name: GetUserImageTypePreferences :many +SELECT type_key FROM user_image_type_preferences +WHERE user_id = $1 +ORDER BY sort_order ASC; + +-- name: DeleteUserImageTypePreferences :exec +DELETE FROM user_image_type_preferences WHERE user_id = $1; + +-- name: CreateUserImageTypePreferences :copyfrom +INSERT INTO user_image_type_preferences (user_id, type_key, sort_order) VALUES ($1, $2, $3); + +-- name: GetUserImageTypeGroupPreferences :many +SELECT group_key FROM user_image_type_group_preferences +WHERE user_id = $1 +ORDER BY sort_order ASC; + +-- name: DeleteUserImageTypeGroupPreferences :exec +DELETE FROM user_image_type_group_preferences WHERE user_id = $1; + +-- name: CreateUserImageTypeGroupPreferences :copyfrom +INSERT INTO user_image_type_group_preferences (user_id, group_key, sort_order) VALUES ($1, $2, $3); + +-- name: GetAllImageTypeConflicts :many +SELECT type_key, conflicts_with_key FROM image_type_conflicts; + +-- Enabling + +-- name: SetImageTypeGroupsEnabled :exec +-- Takes the complete set of disabled keys, so a group absent from the list is +-- enabled. A type added to the vocabulary later is therefore on by default, +-- which an "enabled set" would have silently reversed. +-- +-- COALESCE because an array arriving as SQL NULL would set enabled = NULL on +-- every row rather than enabling them: NOT (key = ANY(NULL)) is NULL, not true. +UPDATE image_type_groups SET enabled = NOT ("key" = ANY(COALESCE(sqlc.arg(disabled)::text[], '{}'))); + +-- name: SetImageTypesEnabled :exec +UPDATE image_types SET enabled = NOT ("key" = ANY(COALESCE(sqlc.arg(disabled)::text[], '{}'))); diff --git a/internal/queries/sql/performer.sql b/internal/queries/sql/performer.sql index 1c90ae29b..e6557605d 100644 --- a/internal/queries/sql/performer.sql +++ b/internal/queries/sql/performer.sql @@ -225,7 +225,7 @@ WHERE performer_images.performer_id = $1; DELETE FROM performer_images WHERE performer_id = $1; -- name: CreatePerformerImages :copyfrom -INSERT INTO performer_images (performer_id, image_id) VALUES ($1, $2); +INSERT INTO performer_images (performer_id, image_id, date) VALUES ($1, $2, $3); -- name: CreatePerformerAliases :copyfrom INSERT INTO performer_aliases (performer_id, alias) VALUES ($1, $2); diff --git a/internal/service/edit/edit.go b/internal/service/edit/edit.go index d38d8158a..ba33a9f6f 100644 --- a/internal/service/edit/edit.go +++ b/internal/service/edit/edit.go @@ -76,44 +76,3 @@ func (m *mutator) CreateComment(userID uuid.UUID, comment *string) error { type editApplyer interface { apply() error } - -func urlCompare(subject []models.URL, against []models.URL) (added []models.URL, missing []models.URL) { - for _, s := range subject { - newMod := true - for _, a := range against { - if s.URL == a.URL && s.SiteID == a.SiteID { - newMod = false - } - } - - for _, a := range added { - if s.URL == a.URL && s.SiteID == a.SiteID { - newMod = false - } - } - - if newMod { - added = append(added, s) - } - } - - for _, s := range against { - removedMod := true - for _, a := range subject { - if s.URL == a.URL && s.SiteID == a.SiteID { - removedMod = false - } - } - - for _, a := range missing { - if s.URL == a.URL && s.SiteID == a.SiteID { - removedMod = false - } - } - - if removedMod { - missing = append(missing, s) - } - } - return -} diff --git a/internal/service/edit/performer.go b/internal/service/edit/performer.go index e0760a028..3f4495948 100644 --- a/internal/service/edit/performer.go +++ b/internal/service/edit/performer.go @@ -10,6 +10,7 @@ import ( "github.com/stashapp/stash-box/internal/converter" "github.com/stashapp/stash-box/internal/models" "github.com/stashapp/stash-box/internal/queries" + "github.com/stashapp/stash-box/internal/service/imagetype" "github.com/stashapp/stash-box/pkg/utils" ) @@ -149,6 +150,22 @@ func (m *PerformerEditProcessor) createEdit(input models.PerformerEditInput, inp performerEdit.New.AddedUrls = input.Details.Urls performerEdit.New.DraftID = input.Details.DraftID + for _, entry := range input.Details.ImageTypes { + for _, imageType := range entry.Types { + performerEdit.New.AddedImageTypes = append(performerEdit.New.AddedImageTypes, models.ImageTypeAssignment{ + ImageID: entry.ImageID, + Type: imageType, + }) + } + + if entry.Date != nil { + performerEdit.New.ImageDates = append(performerEdit.New.ImageDates, models.ImageDate{ + ImageID: entry.ImageID, + Date: entry.Date, + }) + } + } + return m.edit.SetData(*performerEdit) } @@ -367,6 +384,12 @@ func (m *PerformerEditProcessor) diffRelationships(performerEdit *models.Perform } } + if input.Details.ImageTypes != nil || inputArgs.Field("image_types").IsNull() { + if err := m.diffImageTypes(performerEdit, performerID, input.Details.ImageTypes); err != nil { + return err + } + } + return nil } @@ -436,7 +459,7 @@ func (m *PerformerEditProcessor) diffURLs(performerEdit *models.PerformerEditDat SiteID: url.SiteID, }) } - performerEdit.New.AddedUrls, performerEdit.New.RemovedUrls = urlCompare(newURLs, urls) + performerEdit.New.AddedUrls, performerEdit.New.RemovedUrls = utils.SliceCompare(newURLs, urls) return nil } @@ -456,6 +479,99 @@ func (m *PerformerEditProcessor) diffImages(performerEdit *models.PerformerEditD return nil } +// diffImageTypes is the tuple analogue of diffImages, over (image_id, type) +// rather than a bare image id: retagging is one removed tuple plus one added +// tuple, which is genuinely what happened +// +// This is the edit path's half of the table on ImageAssignmentInput in +// graphql/schema/types/image_type.graphql, which is where the whole contract +// is written down. The direct path implements the same table in +// performer/joins.go's resolveAssignments, and nothing makes the two agree +// except that table. +// +// Called only when the caller stated the field: see the IsNull check at the +// call site, which is what lets this path treat an explicit null as "clear" +// where the direct path cannot +func (m *PerformerEditProcessor) diffImageTypes(performerEdit *models.PerformerEditData, performerID uuid.UUID, submitted []models.ImageAssignmentInput) error { + rows, err := m.queries.FindImageTypesByPerformerIds(m.context, []uuid.UUID{performerID}) + if err != nil { + return err + } + + named := make(map[uuid.UUID]struct{}, len(submitted)) + var newAssignments []models.ImageTypeAssignment + for _, entry := range submitted { + named[entry.ImageID] = struct{}{} + for _, imageType := range entry.Types { + newAssignments = append(newAssignments, models.ImageTypeAssignment{ + ImageID: entry.ImageID, + Type: imageType, + }) + } + } + + var current []models.ImageTypeAssignment + for _, row := range rows { + // A non-empty list is authoritative only over the images it names, so + // an image left out of it must not enter the diff at all or it would + // be stripped. + // null and the empty list name nothing and clear everything, matching how image_ids behaves + if len(submitted) > 0 { + if _, mentioned := named[row.ImageID]; !mentioned { + continue + } + } + + current = append(current, models.ImageTypeAssignment{ + ImageID: row.ImageID, + Type: models.ImageTypeEnum(row.TypeKey), + }) + } + + performerEdit.New.AddedImageTypes, performerEdit.New.RemovedImageTypes = utils.SliceCompare(newAssignments, current) + + return m.diffImageDates(performerEdit, performerID, submitted) +} + +// diffImageDates records only the dates the edit changes. A date is +// single-valued, so this is an override list rather than added/removed tuples, +// and an image the submission does not name is simply absent from it +func (m *PerformerEditProcessor) diffImageDates(performerEdit *models.PerformerEditData, performerID uuid.UUID, submitted []models.ImageAssignmentInput) error { + rows, err := m.queries.FindImageDatesByPerformerIds(m.context, []uuid.UUID{performerID}) + if err != nil { + return err + } + + currentDates := make(map[uuid.UUID]*string, len(rows)) + for _, row := range rows { + currentDates[row.ImageID] = row.Date + } + + var changed []models.ImageDate + for _, entry := range submitted { + current := currentDates[entry.ImageID] + if ptrEqual(current, entry.Date) { + continue + } + + changed = append(changed, models.ImageDate{ + ImageID: entry.ImageID, + Date: entry.Date, + }) + } + + performerEdit.New.ImageDates = changed + + return nil +} + +func ptrEqual(a, b *string) bool { + if a == nil || b == nil { + return a == b + } + return *a == *b +} + func (m *PerformerEditProcessor) SoftDelete(performer models.Performer) (*models.Performer, error) { // Delete joins if err := m.queries.DeletePerformerAliases(m.context, performer.ID); err != nil { @@ -705,6 +821,29 @@ func (m *PerformerEditProcessor) updateImagesFromEdit(performerID uuid.UUID, dat return err } + // Resolved before the delete below, which cascades the assignments away + // through performer_image_types' composite foreign key. Reading after it + // would resolve against an emptied table and quietly drop every label, + // including on edits that never mention images + dbTypes, err := m.queries.GetImageTypesForEdit(m.context, m.edit.ID) + if err != nil { + return err + } + + // Dates are a column on the rows about to be deleted, so unlike the + // assignments they are not merely cascaded but truncated: anything not + // written back below is gone. Resolved before the delete for the same + // reason the assignments are + dbDates, err := m.queries.GetImageDatesForEdit(m.context, m.edit.ID) + if err != nil { + return err + } + + dates := make(map[uuid.UUID]*string, len(dbDates)) + for _, dbDate := range dbDates { + dates[dbDate.ImageID] = dbDate.Date + } + if err := m.queries.DeletePerformerImages(m.context, performerID); err != nil { return err } @@ -714,9 +853,53 @@ func (m *PerformerEditProcessor) updateImagesFromEdit(performerID uuid.UUID, dat images = append(images, queries.CreatePerformerImagesParams{ ImageID: image.ID, PerformerID: performerID, + Date: dates[image.ID], + }) + } + + if _, err := m.queries.CreatePerformerImages(m.context, images); err != nil { + return err + } + + if err := imagetype.ValidateCombinations(m.context, m.queries, mergedAssignments(dbTypes)); err != nil { + return err + } + + // After the join rows, which the composite foreign key requires to exist. + var imageTypes []queries.CreatePerformerImageTypesParams + for _, dbType := range dbTypes { + imageTypes = append(imageTypes, queries.CreatePerformerImageTypesParams{ + PerformerID: performerID, + ImageID: dbType.ImageID, + TypeKey: dbType.TypeKey, }) } - _, err = m.queries.CreatePerformerImages(m.context, images) + _, err = m.queries.CreatePerformerImageTypes(m.context, imageTypes) return err } + +// mergedAssignments regroups the resolved tuples into one entry per image, +// which is the shape the validator reasons in: the rules are about what a +// single image ends up carrying +func mergedAssignments(dbTypes []queries.GetImageTypesForEditRow) []models.ImageAssignmentInput { + byImage := make(map[uuid.UUID][]models.ImageTypeEnum) + order := make([]uuid.UUID, 0, len(dbTypes)) + + for _, dbType := range dbTypes { + if _, seen := byImage[dbType.ImageID]; !seen { + order = append(order, dbType.ImageID) + } + byImage[dbType.ImageID] = append(byImage[dbType.ImageID], models.ImageTypeEnum(dbType.TypeKey)) + } + + assignments := make([]models.ImageAssignmentInput, 0, len(order)) + for _, imageID := range order { + assignments = append(assignments, models.ImageAssignmentInput{ + ImageID: imageID, + Types: byImage[imageID], + }) + } + + return assignments +} diff --git a/internal/service/edit/scene.go b/internal/service/edit/scene.go index 8af49c6a9..a3be764bd 100644 --- a/internal/service/edit/scene.go +++ b/internal/service/edit/scene.go @@ -139,7 +139,7 @@ func (m *SceneEditProcessor) diffURLs(sceneEdit *models.SceneEditData, sceneID u SiteID: url.SiteID, }) } - sceneEdit.New.AddedUrls, sceneEdit.New.RemovedUrls = urlCompare(newURLs, urls) + sceneEdit.New.AddedUrls, sceneEdit.New.RemovedUrls = utils.SliceCompare(newURLs, urls) return nil } diff --git a/internal/service/edit/service.go b/internal/service/edit/service.go index a669bd5e1..8d5e3d0cf 100644 --- a/internal/service/edit/service.go +++ b/internal/service/edit/service.go @@ -564,6 +564,52 @@ func (s *Edit) GetMergedImages(ctx context.Context, id uuid.UUID) ([]models.Imag return converter.ImagesToModels(res), nil } +// GetMergedTypedImages is the gallery this edit results in: each surviving +// image with the labels and date it will carry once applied. +// +// The same three queries the apply path uses, so a reviewer looking at the +// diff and the writer applying it are reading one answer. Deliberately not +// assembled from the added/removed tuples in the payload, which say what +// changes rather than what results, and which cannot be resolved into a final +// set without the current state anyway. +func (s *Edit) GetMergedTypedImages(ctx context.Context, id uuid.UUID) ([]models.TypedImage, error) { + images, err := s.queries.GetImagesForEdit(ctx, id) + if err != nil { + return nil, err + } + + dbTypes, err := s.queries.GetImageTypesForEdit(ctx, id) + if err != nil { + return nil, err + } + typesByImage := make(map[uuid.UUID][]models.ImageTypeEnum, len(images)) + for _, row := range dbTypes { + typesByImage[row.ImageID] = append(typesByImage[row.ImageID], models.ImageTypeEnum(row.TypeKey)) + } + + dbDates, err := s.queries.GetImageDatesForEdit(ctx, id) + if err != nil { + return nil, err + } + dateByImage := make(map[uuid.UUID]*string, len(images)) + for _, row := range dbDates { + dateByImage[row.ImageID] = row.Date + } + + // Image order comes from the query, so the diff lists them the same way + // twice running rather than following a map. + resolved := converter.ImagesToModels(images) + typed := make([]models.TypedImage, 0, len(resolved)) + for i := range resolved { + typed = append(typed, models.TypedImage{ + Image: &resolved[i], + Types: typesByImage[resolved[i].ID], + Date: dateByImage[resolved[i].ID], + }) + } + return typed, nil +} + func (s *Edit) GetMergedPerformerAliases(ctx context.Context, id uuid.UUID) ([]string, error) { return s.queries.GetEditPerformerAliases(ctx, id) } diff --git a/internal/service/edit/studio.go b/internal/service/edit/studio.go index a59666cd6..a96760ec2 100644 --- a/internal/service/edit/studio.go +++ b/internal/service/edit/studio.go @@ -94,7 +94,7 @@ func (m *StudioEditProcessor) diffURLs(studioEdit *models.StudioEditData, studio SiteID: url.SiteID, }) } - studioEdit.New.AddedUrls, studioEdit.New.RemovedUrls = urlCompare(newURLs, urls) + studioEdit.New.AddedUrls, studioEdit.New.RemovedUrls = utils.SliceCompare(newURLs, urls) return nil } diff --git a/internal/service/edit/validate.go b/internal/service/edit/validate.go index 597755a50..ccb1bfd0d 100644 --- a/internal/service/edit/validate.go +++ b/internal/service/edit/validate.go @@ -9,6 +9,7 @@ import ( "github.com/gofrs/uuid" "github.com/stashapp/stash-box/internal/models" "github.com/stashapp/stash-box/internal/queries" + "github.com/stashapp/stash-box/internal/service/imagetype" "github.com/stashapp/stash-box/pkg/utils" ) @@ -122,9 +123,52 @@ func validatePerformerEditInput(ctx context.Context, queries *queries.Queries, i } } + if input.Details.ImageTypes != nil { + imageIDs, err := editImageSet(ctx, queries, input) + if err != nil { + return err + } + + var assigned imagetype.AssignedTypes + if input.Edit != nil && input.Edit.ID != nil { + assigned, err = imagetype.PerformerAssignedTypes(ctx, queries, *input.Edit.ID) + if err != nil { + return err + } + } + + if err := imagetype.ValidateAssignments(ctx, queries, models.ImageTypeScopeEnumPerformer, input.Details.ImageTypes, imageIDs, assigned); err != nil { + return err + } + } + return validateURLs(ctx, queries, input.Details.Urls) } +// editImageSet is the image set the edit will end up with, which is what +// assignments have to be checked against. image_ids is authoritative when +// given; when it is absent the entity keeps the images it already has. +func editImageSet(ctx context.Context, queries *queries.Queries, input models.PerformerEditInput) ([]uuid.UUID, error) { + if input.Details.ImageIds != nil { + return input.Details.ImageIds, nil + } + + if input.Edit == nil || input.Edit.ID == nil { + return nil, nil + } + + images, err := queries.GetPerformerImages(ctx, *input.Edit.ID) + if err != nil { + return nil, err + } + + imageIDs := make([]uuid.UUID, len(images)) + for i, image := range images { + imageIDs[i] = image.ID + } + return imageIDs, nil +} + func validateDraftID(ctx context.Context, queries *queries.Queries, draftID uuid.UUID, editID uuid.UUID, update bool) error { if !update { _, err := queries.FindDraft(ctx, draftID) diff --git a/internal/service/factory.go b/internal/service/factory.go index 8fd919484..e0dbeb513 100644 --- a/internal/service/factory.go +++ b/internal/service/factory.go @@ -20,11 +20,13 @@ package service import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/stashapp/stash-box/internal/email" + "github.com/stashapp/stash-box/internal/image/croptemplate" "github.com/stashapp/stash-box/internal/queries" "github.com/stashapp/stash-box/internal/service/draft" "github.com/stashapp/stash-box/internal/service/edit" "github.com/stashapp/stash-box/internal/service/fingerprint" "github.com/stashapp/stash-box/internal/service/image" + "github.com/stashapp/stash-box/internal/service/imagetype" "github.com/stashapp/stash-box/internal/service/invite" "github.com/stashapp/stash-box/internal/service/mod_audit" "github.com/stashapp/stash-box/internal/service/notification" @@ -42,14 +44,17 @@ type Factory struct { db *pgxpool.Pool withTxn queries.WithTxnFunc emailMgr *email.Manager + + cropTemplates *croptemplate.Loader } // NewFactory creates a new service factory with the given database pool and email manager func NewFactory(pool *pgxpool.Pool, emailMgr *email.Manager) *Factory { return &Factory{ - db: pool, - withTxn: createWithTxnFunc(pool), - emailMgr: emailMgr, + db: pool, + withTxn: createWithTxnFunc(pool), + emailMgr: emailMgr, + cropTemplates: croptemplate.NewLoader(), } } @@ -98,6 +103,11 @@ func (f *Factory) Image() *image.Image { return image.NewImage(queries.New(f.db), f.withTxn) } +// ImageType returns an ImageTypeService instance +func (f *Factory) ImageType() *imagetype.ImageType { + return imagetype.NewImageType(queries.New(f.db), f.withTxn) +} + // Draft returns a DraftService instance func (f *Factory) Draft() *draft.Draft { return draft.NewDraft(queries.New(f.db), f.withTxn) @@ -121,3 +131,8 @@ func (f *Factory) ModAudit() *mod_audit.ModAuditService { func (f *Factory) Fingerprint() *fingerprint.Fingerprint { return fingerprint.New(queries.New(f.db)) } + +// CropTemplates returns the shared crop template loader. +func (f *Factory) CropTemplates() *croptemplate.Loader { + return f.cropTemplates +} diff --git a/internal/service/image/service.go b/internal/service/image/service.go index c21dea0db..856a708ad 100644 --- a/internal/service/image/service.go +++ b/internal/service/image/service.go @@ -18,6 +18,10 @@ import ( "github.com/stashapp/stash-box/internal/storage" ) +const maxUploadBytes = int64(10 * 1024 * 1024) + +var errUploadTooBig = errors.New("file too big") + type Image struct { queries *queries.Queries withTxn queries.WithTxnFunc @@ -60,14 +64,37 @@ func (s *Image) Create(ctx context.Context, input models.ImageCreateInput) (*mod // handle image upload if input.File != nil { - if input.File.Size > int64(10*1024*1024) { - return nil, errors.New("file too big") + if input.File.Size > maxUploadBytes { + return nil, errUploadTooBig } + // ReadFull rather than Read: a single Read may return early on a + // multipart upload, and a short read here yields a truncated image + // that still decodes and then gets cropped and stored as the master file := make([]byte, input.File.Size) - if _, err := input.File.File.Read(file); err != nil { + if _, err := io.ReadFull(input.File.File, file); err != nil { return nil, err } + + // Before the checksum, so everything downstream - deduplication, + // dimensions, what gets written - is about the image that will + // actually exist. Two contributors cropping the same source to the + // same frame produce the same bytes and land as one image, which is + // the property a crop done in the browser would quietly lose + if input.Crop != nil { + cropped, err := cropUpload(file, *input.Crop) + if err != nil { + return nil, err + } + // Checked again on the way out: the limit is about what gets + // stored, and re-encoding is not guaranteed to shrink what it was + // given. Straightening a flat PNG can grow it outright + if int64(len(cropped)) > maxUploadBytes { + return nil, errUploadTooBig + } + file = cropped + } + fileReader := bytes.NewReader(file) checksum, err := calculateChecksum(fileReader) diff --git a/internal/service/image/utils.go b/internal/service/image/utils.go index 65c0fa77d..986d1419d 100644 --- a/internal/service/image/utils.go +++ b/internal/service/image/utils.go @@ -14,6 +14,7 @@ import ( issvg "github.com/h2non/go-is-svg" + imagepkg "github.com/stashapp/stash-box/internal/image" "github.com/stashapp/stash-box/internal/models" ) @@ -61,3 +62,23 @@ func calculateChecksum(file io.Reader) (string, error) { checksum := hex.EncodeToString(hasher.Sum(nil)) return checksum, nil } + +// cropUpload cuts an upload down to the frame a client asked for +func cropUpload(file []byte, input models.ImageCropInput) ([]byte, error) { + rect := imagepkg.CropRect{ + X: input.X, + Y: input.Y, + Width: input.Width, + Height: input.Height, + } + if input.Angle != nil { + rect.Angle = *input.Angle + } + + // Image doesn't need to change + if rect.IsIdentity() { + return file, nil + } + + return imagepkg.Crop(file, rect) +} diff --git a/internal/service/imagetype/order.go b/internal/service/imagetype/order.go new file mode 100644 index 000000000..372bdd958 --- /dev/null +++ b/internal/service/imagetype/order.go @@ -0,0 +1,133 @@ +package imagetype + +import ( + "context" + "fmt" + + "github.com/stashapp/stash-box/internal/models" + "github.com/stashapp/stash-box/internal/queries" +) + +// UpdateOrder rewrites sort_order on both tables from list position and +// returns the reordered vocabulary. This is the whole admin write surface: +// the taxonomy itself is fixed, and only its ordering can be customized. +func (s *ImageType) UpdateOrder(ctx context.Context, input models.ImageTypeOrderInput) ([]models.ImageTypeGroup, error) { + if err := validateComplete("groups", input.Groups, models.AllImageTypeGroupEnum); err != nil { + return nil, err + } + if err := validateComplete("types", input.Types, models.AllImageTypeEnum); err != nil { + return nil, err + } + + err := s.withTxn(func(tx *queries.Queries) error { + // Which group a type belongs to is read rather than derived from the + // key prefix: the prefix rule is asserted by a test, not by the + // schema, so trusting it here would let a seeding mistake renumber + // the wrong group + dbTypes, err := tx.GetAllImageTypes(ctx) + if err != nil { + return err + } + + groupOfType := make(map[string]string, len(dbTypes)) + for _, dbType := range dbTypes { + groupOfType[dbType.Key] = dbType.GroupKey + } + + for i, key := range input.Groups { + if err := tx.UpdateImageTypeGroupSortOrder(ctx, queries.UpdateImageTypeGroupSortOrderParams{ + Key: string(key), + SortOrder: i, + }); err != nil { + return err + } + } + + // Only position within a group counts, so each group's types are + // numbered from zero in the order they appear. The submitted list may + // therefore interleave groups freely + nextInGroup := make(map[string]int, len(input.Groups)) + for _, key := range input.Types { + groupKey, ok := groupOfType[string(key)] + if !ok { + return fmt.Errorf("image type %s is not seeded", key) + } + + if err := tx.UpdateImageTypeSortOrder(ctx, queries.UpdateImageTypeSortOrderParams{ + Key: string(key), + SortOrder: nextInGroup[groupKey], + }); err != nil { + return err + } + nextInGroup[groupKey]++ + } + + return nil + }) + if err != nil { + return nil, err + } + + return s.Groups(ctx, nil, true) +} + +// validateComplete rejects anything short of a total ordering. Every submitted +// value being known, none repeated, and the count matching together mean the +// submitted list is exactly the full set. No need to diff here. +func validateComplete[T comparable](field string, submitted []T, all []T) error { + known := make(map[T]struct{}, len(all)) + for _, value := range all { + known[value] = struct{}{} + } + + seen := make(map[T]struct{}, len(submitted)) + for _, value := range submitted { + if _, ok := known[value]; !ok { + return fmt.Errorf("%s contains unknown value %v", field, value) + } + if _, duplicate := seen[value]; duplicate { + return fmt.Errorf("%s lists %v more than once", field, value) + } + seen[value] = struct{}{} + } + + if len(submitted) != len(all) { + return fmt.Errorf("%s must list all %d values in order, got %d", field, len(all), len(submitted)) + } + + return nil +} + +// SetEnabled records which parts of the vocabulary this instance uses +// +// Takes the complete disabled set rather than a per-key toggle, so the write +// is idempotent and a client cannot half-apply one. Nothing is deleted: rows +// stay, assignments stay, and the foreign keys from performer_image_types stay +// valid, so switching a group back on restores every label made while it was +// in use +func (s *ImageType) SetEnabled(ctx context.Context, input models.ImageTypeEnabledInput) ([]models.ImageTypeGroup, error) { + groups := make([]string, len(input.DisabledGroups)) + for i, group := range input.DisabledGroups { + groups[i] = string(group) + } + + types := make([]string, len(input.DisabledTypes)) + for i, imageType := range input.DisabledTypes { + types[i] = string(imageType) + } + + err := s.withTxn(func(tx *queries.Queries) error { + if err := tx.SetImageTypeGroupsEnabled(ctx, groups); err != nil { + return err + } + return tx.SetImageTypesEnabled(ctx, types) + }) + if err != nil { + return nil, err + } + + // Disabled entries included: this is the admin screen's own read-back, and + // it has to keep showing what it just switched off so that it can be switched + // back on + return s.Groups(ctx, nil, true) +} diff --git a/internal/service/imagetype/preference_test.go b/internal/service/imagetype/preference_test.go new file mode 100644 index 000000000..f2c463dae --- /dev/null +++ b/internal/service/imagetype/preference_test.go @@ -0,0 +1,251 @@ +package imagetype + +import ( + "slices" + "sort" + "testing" + + "github.com/stashapp/stash-box/internal/image" + "github.com/stashapp/stash-box/internal/models" + "github.com/stashapp/stash-box/internal/queries" +) + +// groupOrder and typeOrder read a vocabulary back as the two orderings it +// encodes. The positions themselves are an implementation detail; what decides +// what a viewer sees is which dimension is compared before which, and which +// type wins inside one +func groupOrder(v *Vocabulary) []string { + groups := make([]string, 0, len(v.groupPosition)) + for key := range v.groupPosition { + groups = append(groups, key) + } + sort.Slice(groups, func(a, b int) bool { + return v.groupPosition[groups[a]] < v.groupPosition[groups[b]] + }) + return groups +} + +func typeOrder(v *Vocabulary, group string) []models.ImageTypeEnum { + var types []models.ImageTypeEnum + for imageType, groupKey := range v.typeGroup { + if groupKey == group { + types = append(types, imageType) + } + } + sort.Slice(types, func(a, b int) bool { + return v.typePosition[types[a]] < v.typePosition[types[b]] + }) + return types +} + +// A type preference reorders within dimensions and must not touch the +// dimensions themselves, or a user expressing a taste in crops would silently +// change what is compared first +func TestWithPreferenceLeavesGroupsAndOtherDimensionsAlone(t *testing.T) { + v := testVocabulary().withPreference([]models.ImageTypeEnum{ + models.ImageTypeEnumCropFullBody, + }) + + if got, want := groupOrder(v), []string{"CROP", "POSE"}; !slices.Equal(got, want) { + t.Errorf("group order = %v, want %v", got, want) + } + + want := []models.ImageTypeEnum{models.ImageTypeEnumViewFront, models.ImageTypeEnumViewBack} + if got := typeOrder(v, "POSE"); !slices.Equal(got, want) { + t.Errorf("POSE order = %v, want %v", got, want) + } +} + +// Preferences are stored per user and the vocabulary can change under them: an +// admin switches a type off and every preference naming it is now stale. A +// stored preference must not put a type back into a vocabulary that no longer +// ranks it +// +// Two things currently stop it: the explicit skip, and the fact that +// positions are handed out over v.groupPosition, which a phantom group is not +// in. So this asserts the property rather than either mechanism, and stays +// honest if one of them goes +func TestWithPreferenceIgnoresTypesOutsideTheVocabulary(t *testing.T) { + v := testVocabulary().withPreference([]models.ImageTypeEnum{ + models.ImageTypeEnumDressNude, // not in this vocabulary at all + models.ImageTypeEnumCropBust, + models.ImageTypeEnumCropFullBody, + }) + + want := []models.ImageTypeEnum{ + models.ImageTypeEnumCropBust, + models.ImageTypeEnumCropFullBody, + models.ImageTypeEnumCropFace, // the only one left, in instance order + } + if got := typeOrder(v, "CROP"); !slices.Equal(got, want) { + t.Errorf("CROP order = %v, want %v", got, want) + } + + if _, ranked := v.typePosition[models.ImageTypeEnumDressNude]; ranked { + t.Error("a type outside the vocabulary was given a position") + } +} + +// A repeat cannot be seen in the ordering -- positions 1,2,3 sort exactly as +// 0,1,2, since only the relative order within a group is ever compared. So this +// looks at the positions themselves, which is the only way to tell the dedupe +// is still there. Contrast the group case below, where the same gap is fatal +func TestWithPreferenceNumbersTypesWithoutGaps(t *testing.T) { + v := testVocabulary().withPreference([]models.ImageTypeEnum{ + models.ImageTypeEnumCropBust, + models.ImageTypeEnumCropBust, // repeat + models.ImageTypeEnumCropFullBody, + }) + + for position, imageType := range typeOrder(v, "CROP") { + if got := v.typePosition[imageType]; got != position { + t.Errorf("%v is at position %d, want %d", imageType, got, position) + } + } +} + +// The group preference is the stronger of the two: type order only breaks ties +// inside a dimension, so moving a dimension decides what is compared at all. +func TestWithGroupPreferenceReordersDimensionsAndKeepsTypeOrder(t *testing.T) { + v := testVocabulary().withGroupPreference([]string{"POSE"}) + + if got, want := groupOrder(v), []string{"POSE", "CROP"}; !slices.Equal(got, want) { + t.Errorf("group order = %v, want %v", got, want) + } + + want := []models.ImageTypeEnum{ + models.ImageTypeEnumCropFace, + models.ImageTypeEnumCropBust, + models.ImageTypeEnumCropFullBody, + } + if got := typeOrder(v, "CROP"); !slices.Equal(got, want) { + t.Errorf("CROP order = %v, want %v", got, want) + } +} + +// An unknown or repeated group is not a cosmetic problem the way an unknown or +// repeated type is. Positions are handed out over the preference list, but the +// tuple is allocated to groupCount, so either one pushes a real group past the +// end of the tuple and Rank panics on an index out of range inside a GraphQL +// resolver, on a stale preference the user cannot see. +// +// Asserted through Rank rather than through groupPosition, because that is +// where it would actually go wrong +func TestWithGroupPreferenceIgnoresUnknownAndRepeatedGroups(t *testing.T) { + for _, tc := range []struct { + name string + preferred []string + }{ + {"a group this instance does not have", []string{"DRESS", "POSE"}}, + {"the same group twice", []string{"POSE", "POSE"}}, + } { + t.Run(tc.name, func(t *testing.T) { + v := testVocabulary().withGroupPreference(tc.preferred) + + if got, want := groupOrder(v), []string{"POSE", "CROP"}; !slices.Equal(got, want) { + t.Errorf("group order = %v, want %v", got, want) + } + if v.groupCount != 2 { + t.Errorf("groupCount = %d, want 2 - the tuple width must not follow a preference", v.groupCount) + } + + // Every group must still index inside a groupCount-wide tuple. + got := v.Rank([]models.ImageTypeEnum{ + models.ImageTypeEnumCropFace, + models.ImageTypeEnumViewBack, + }) + if want := (image.RankTuple{1, 0}); !equal(got, want) { + t.Errorf("Rank = %v, want %v (POSE now leads)", got, want) + } + }) + } +} + +// Each preference carries the other's work forward rather than rebuilding from +// the instance ordering, so the two compose in either order. VocabularyFor +// applies groups first; this is what says it could apply them second and get +// the same vocabulary +func TestPreferencesComposeInEitherOrder(t *testing.T) { + groupsFirst := testVocabulary(). + withGroupPreference([]string{"POSE"}). + withPreference([]models.ImageTypeEnum{models.ImageTypeEnumCropFullBody}) + + typesFirst := testVocabulary(). + withPreference([]models.ImageTypeEnum{models.ImageTypeEnumCropFullBody}). + withGroupPreference([]string{"POSE"}) + + if got, want := groupOrder(typesFirst), groupOrder(groupsFirst); !slices.Equal(got, want) { + t.Errorf("group order depends on the order applied: %v then %v", want, got) + } + for _, group := range []string{"CROP", "POSE"} { + got, want := typeOrder(typesFirst, group), typeOrder(groupsFirst, group) + if !slices.Equal(got, want) { + t.Errorf("%s order depends on the order applied: %v then %v", group, want, got) + } + } + + // And that the composed result is actually both preferences, not one of + // them silently winning + if got, want := groupOrder(groupsFirst), []string{"POSE", "CROP"}; !slices.Equal(got, want) { + t.Errorf("group order = %v, want %v", got, want) + } + if got := typeOrder(groupsFirst, "CROP")[0]; got != models.ImageTypeEnumCropFullBody { + t.Errorf("CROP leads with %v, want the preferred CROP_FULL_BODY", got) + } +} + +// Thumbnails rank against Instance() (resolver_model_performer.go), which is +// what lets the field be the same for every viewer and therefore cacheable. +// Instance() must stay one hop from the unadjusted ordering however many +// preferences are layered on, otherwise if a layer ever pointed at an +// already-adjusted vocabulary thumbnails would quietly become viewer-dependent +func TestInstanceStaysUnadjustedUnderEveryLayer(t *testing.T) { + base := testVocabulary() + + layered := base. + withGroupPreference([]string{"POSE"}). + withPreference([]models.ImageTypeEnum{models.ImageTypeEnumCropFullBody}). + withGroupPreference([]string{"CROP"}). + withPreference([]models.ImageTypeEnum{models.ImageTypeEnumViewBack}) + + if layered.Instance() != base { + t.Error("Instance() no longer reaches the vocabulary the preferences were built from") + } + + if got, want := groupOrder(base), []string{"CROP", "POSE"}; !slices.Equal(got, want) { + t.Errorf("the instance ordering was mutated: groups %v, want %v", got, want) + } + want := []models.ImageTypeEnum{ + models.ImageTypeEnumCropFace, + models.ImageTypeEnumCropBust, + models.ImageTypeEnumCropFullBody, + } + if got := typeOrder(base, "CROP"); !slices.Equal(got, want) { + t.Errorf("the instance ordering was mutated: CROP %v, want %v", got, want) + } +} + +// Position is what counts and the primary key forbids repeats, so a later +// duplicate says nothing the first did not +func TestPreferenceParamsNumbersInOrderAndDropsRepeats(t *testing.T) { + params := preferenceParams( + []models.ImageTypeEnum{ + models.ImageTypeEnumCropFace, + models.ImageTypeEnumCropBust, + models.ImageTypeEnumCropFace, + models.ImageTypeEnumCropFullBody, + }, + func(key string, sortOrder int) queries.CreateUserImageTypePreferencesParams { + return queries.CreateUserImageTypePreferencesParams{TypeKey: key, SortOrder: sortOrder} + }, + ) + + want := []queries.CreateUserImageTypePreferencesParams{ + {TypeKey: "CROP_FACE", SortOrder: 0}, + {TypeKey: "CROP_BUST", SortOrder: 1}, + {TypeKey: "CROP_FULL_BODY", SortOrder: 2}, + } + if !slices.Equal(params, want) { + t.Errorf("preferenceParams = %v, want %v", params, want) + } +} diff --git a/internal/service/imagetype/rank.go b/internal/service/imagetype/rank.go new file mode 100644 index 000000000..e9f47f00c --- /dev/null +++ b/internal/service/imagetype/rank.go @@ -0,0 +1,406 @@ +package imagetype + +import ( + "context" + "sort" + + "github.com/gofrs/uuid" + + "github.com/stashapp/stash-box/internal/image" + "github.com/stashapp/stash-box/internal/models" + "github.com/stashapp/stash-box/internal/queries" +) + +// Vocabulary is the ordering half of the image type vocabulary: which dimension +// each type belongs to, and how the instance ranks them. Read once per request, +// since every performer in a result ranks against the same one +type Vocabulary struct { + // Tuple position of each group, in group priority order + groupPosition map[string]int + groupCount int + // Priority of each type within its group + typePosition map[models.ImageTypeEnum]int + typeGroup map[models.ImageTypeEnum]string + // The unadjusted instance ordering, when this one carries a user's + // preference: is nil when this is already the instance ordering + instance *Vocabulary +} + +func (v *Vocabulary) Instance() *Vocabulary { + if v.instance != nil { + return v.instance + } + return v +} + +// VocabularyFor reads the ordering as one user sees it: the instance ordering +// with that user's preference applied. Callers pass the viewer, which is how +// scraping gets its owner's ordering without changing its query +func (s *ImageType) VocabularyFor(ctx context.Context, userID uuid.UUID) (*Vocabulary, error) { + vocabulary, err := loadVocabulary(ctx, s.queries) + if err != nil { + return nil, err + } + + preferred, err := s.queries.GetUserImageTypePreferences(ctx, userID) + if err != nil { + return nil, err + } + + preferredGroups, err := s.queries.GetUserImageTypeGroupPreferences(ctx, userID) + if err != nil { + return nil, err + } + + if len(preferred) == 0 && len(preferredGroups) == 0 { + return vocabulary, nil + } + + // The two compose in either order; groups go first as the stronger one + if len(preferredGroups) > 0 { + vocabulary = vocabulary.withGroupPreference(preferredGroups) + } + + if len(preferred) > 0 { + types := make([]models.ImageTypeEnum, len(preferred)) + for i, key := range preferred { + types[i] = models.ImageTypeEnum(key) + } + vocabulary = vocabulary.withPreference(types) + } + + return vocabulary, nil +} + +// withPreference reorders types within each group, leaving the group order to +// whatever the caller already decided. Types the user did not list trail the +// ones they did, in instance order, so a partial preference is well defined +func (v *Vocabulary) withPreference(preferred []models.ImageTypeEnum) *Vocabulary { + preferredInGroup := make(map[string][]models.ImageTypeEnum, len(v.groupPosition)) + listed := make(map[models.ImageTypeEnum]struct{}, len(preferred)) + + for _, imageType := range preferred { + groupKey, known := v.typeGroup[imageType] + if !known { + continue + } + if _, duplicate := listed[imageType]; duplicate { + continue + } + listed[imageType] = struct{}{} + preferredInGroup[groupKey] = append(preferredInGroup[groupKey], imageType) + } + + // Instance order within each group, to fill in behind the listed ones + remainingInGroup := make(map[string][]models.ImageTypeEnum, len(v.groupPosition)) + for imageType, groupKey := range v.typeGroup { + if _, wasListed := listed[imageType]; wasListed { + continue + } + remainingInGroup[groupKey] = append(remainingInGroup[groupKey], imageType) + } + for groupKey := range remainingInGroup { + remaining := remainingInGroup[groupKey] + sort.Slice(remaining, func(a, b int) bool { + return v.typePosition[remaining[a]] < v.typePosition[remaining[b]] + }) + } + + adjusted := &Vocabulary{ + groupPosition: v.groupPosition, + groupCount: v.groupCount, + typePosition: make(map[models.ImageTypeEnum]int, len(v.typePosition)), + typeGroup: v.typeGroup, + // Instance() must stay unadjusted however many preferences are layered on + instance: v.Instance(), + } + + for groupKey := range v.groupPosition { + position := 0 + for _, imageType := range append(preferredInGroup[groupKey], remainingInGroup[groupKey]...) { + adjusted.typePosition[imageType] = position + position++ + } + } + + return adjusted +} + +// withGroupPreference reorders the dimensions themselves, deciding what is +// compared before what. Groups the user did not list trail the ones they did, +// in instance order, exactly as unlisted types do +// +// The stronger of the two preferences: type order only breaks ties inside a +// dimension. Thumbnails are unaffected bacause they rank against Instance() +// which keeps them the same for every viewer +func (v *Vocabulary) withGroupPreference(preferred []string) *Vocabulary { + ordered := make([]string, 0, len(v.groupPosition)) + listed := make(map[string]struct{}, len(preferred)) + + for _, groupKey := range preferred { + if _, known := v.groupPosition[groupKey]; !known { + continue + } + if _, duplicate := listed[groupKey]; duplicate { + continue + } + listed[groupKey] = struct{}{} + ordered = append(ordered, groupKey) + } + + var remaining []string + for groupKey := range v.groupPosition { + if _, wasListed := listed[groupKey]; !wasListed { + remaining = append(remaining, groupKey) + } + } + sort.Slice(remaining, func(a, b int) bool { + return v.groupPosition[remaining[a]] < v.groupPosition[remaining[b]] + }) + + adjusted := &Vocabulary{ + groupPosition: make(map[string]int, len(v.groupPosition)), + groupCount: v.groupCount, + typePosition: v.typePosition, + typeGroup: v.typeGroup, + // Points at the instance ordering, not an already adjusted vocabulary + instance: v.Instance(), + } + + for position, groupKey := range append(ordered, remaining...) { + adjusted.groupPosition[groupKey] = position + } + + return adjusted +} + +func loadVocabulary(ctx context.Context, q *queries.Queries) (*Vocabulary, error) { + dbGroups, err := q.GetAllImageTypeGroups(ctx) + if err != nil { + return nil, err + } + + dbTypes, err := q.GetAllImageTypes(ctx) + if err != nil { + return nil, err + } + + // Disabled entries are left out entirely rather than ranked last: Rank skips + // whatever is missing from typeGroup, so no branch is needed in the + // comparison. Assignments made while a type was enabled stay in the database + // and stop counting, which makes re-enabling lossless + enabledGroups := make(map[string]bool, len(dbGroups)) + vocabulary := &Vocabulary{ + groupPosition: make(map[string]int, len(dbGroups)), + typePosition: make(map[models.ImageTypeEnum]int, len(dbTypes)), + typeGroup: make(map[models.ImageTypeEnum]string, len(dbTypes)), + } + + // GetAllImageTypeGroups orders by sort_order, so position is priority. + // Assigned over enabled groups only, so a tuple has no gap where a disabled + // dimension used to be + for _, dbGroup := range dbGroups { + enabledGroups[dbGroup.Key] = dbGroup.Enabled + if !dbGroup.Enabled { + continue + } + vocabulary.groupPosition[dbGroup.Key] = vocabulary.groupCount + vocabulary.groupCount++ + } + + for _, dbType := range dbTypes { + if !dbType.Enabled || !enabledGroups[dbType.GroupKey] { + continue + } + key := models.ImageTypeEnum(dbType.Key) + vocabulary.typePosition[key] = dbType.SortOrder + vocabulary.typeGroup[key] = dbType.GroupKey + } + + return vocabulary, nil +} + +// Rank builds one image's tuple: for each group, the position of the image's +// best type in it, or Unranked +// +// "Best" rather than "only" because exclusivity is enforced by the validators +// and the seed data, not by the schema: an image that has ended up with two +// values from one group sorts by the better of them rather than by whichever +// the query returned last +func (v *Vocabulary) Rank(types []models.ImageTypeEnum) image.RankTuple { + tuple := make(image.RankTuple, v.groupCount) + for i := range tuple { + tuple[i] = image.Unranked + } + + for _, imageType := range types { + groupKey, known := v.typeGroup[imageType] + if !known { + continue + } + + position, ranked := v.groupPosition[groupKey] + if !ranked { + continue + } + + tuple[position] = min(tuple[position], v.typePosition[imageType]) + } + + return tuple +} + +// RanksByImage builds the tuple for every image an entity carries. Images with +// no assignments are absent from the map, which OrderByType reads as the +// all-Unranked tuple +func (v *Vocabulary) RanksByImage(assignments []models.ImageTypeAssignment) map[uuid.UUID]image.RankTuple { + return v.ranksByImage(assignments, v.Rank) +} + +// ranksByImage regroups flat assignments by image and ranks each one. The two +// callers differ only in which ranking they ask for +func (v *Vocabulary) ranksByImage( + assignments []models.ImageTypeAssignment, + rank func([]models.ImageTypeEnum) image.RankTuple, +) map[uuid.UUID]image.RankTuple { + typesByImage := make(map[uuid.UUID][]models.ImageTypeEnum) + for _, assignment := range assignments { + typesByImage[assignment.ImageID] = append(typesByImage[assignment.ImageID], assignment.Type) + } + + ranks := make(map[uuid.UUID]image.RankTuple, len(typesByImage)) + for imageID, types := range typesByImage { + ranks[imageID] = rank(types) + } + + return ranks +} + +// Preferences returns a user's preferred type order, empty if they have none +func (s *ImageType) Preferences(ctx context.Context, userID uuid.UUID) ([]models.ImageTypeEnum, error) { + keys, err := s.queries.GetUserImageTypePreferences(ctx, userID) + if err != nil { + return nil, err + } + + types := make([]models.ImageTypeEnum, len(keys)) + for i, key := range keys { + types[i] = models.ImageTypeEnum(key) + } + return types, nil +} + +// SetPreferences replaces a user's ordering, types and groups together +// +// The type list is required by the schema, and an empty one clears it. Groups +// are optional, and nil leaves whatever the user already had +func (s *ImageType) SetPreferences( + ctx context.Context, + userID uuid.UUID, + types []models.ImageTypeEnum, + groups []models.ImageTypeGroupEnum, +) error { + return s.withTxn(func(tx *queries.Queries) error { + if err := tx.DeleteUserImageTypePreferences(ctx, userID); err != nil { + return err + } + typeParams := preferenceParams(types, func(key string, sortOrder int) queries.CreateUserImageTypePreferencesParams { + return queries.CreateUserImageTypePreferencesParams{ + UserID: userID, TypeKey: key, SortOrder: sortOrder, + } + }) + if _, err := tx.CreateUserImageTypePreferences(ctx, typeParams); err != nil { + return err + } + + if groups == nil { + return nil + } + + if err := tx.DeleteUserImageTypeGroupPreferences(ctx, userID); err != nil { + return err + } + params := preferenceParams(groups, func(key string, sortOrder int) queries.CreateUserImageTypeGroupPreferencesParams { + return queries.CreateUserImageTypeGroupPreferencesParams{ + UserID: userID, GroupKey: key, SortOrder: sortOrder, + } + }) + _, err := tx.CreateUserImageTypeGroupPreferences(ctx, params) + return err + }) +} + +// preferenceParams numbers a preference list, dropping repeats +// +// Types and groups differ only in which table they land in and what their key +// is called +func preferenceParams[T ~string, P any](values []T, param func(key string, sortOrder int) P) []P { + seen := make(map[T]struct{}, len(values)) + params := make([]P, 0, len(values)) + + for _, value := range values { + if _, duplicate := seen[value]; duplicate { + continue + } + seen[value] = struct{}{} + params = append(params, param(string(value), len(params))) + } + + return params +} + +// GroupPreferences returns a user's preferred group order, empty if none +func (s *ImageType) GroupPreferences(ctx context.Context, userID uuid.UUID) ([]models.ImageTypeGroupEnum, error) { + keys, err := s.queries.GetUserImageTypeGroupPreferences(ctx, userID) + if err != nil { + return nil, err + } + + groups := make([]models.ImageTypeGroupEnum, len(keys)) + for i, key := range keys { + groups[i] = models.ImageTypeGroupEnum(key) + } + return groups, nil +} + +// facePreference is the Crop component given to an image carrying CROP_FACE. +// Seeded sort_order values start at zero, so this sorts below every real crop +// without needing a sentinel +const facePreference = -1 + +// ThumbnailRank ranks an image for use as a recognisable thumbnail: the +// instance tuple with the Crop component overridden so face crops lead their +// dimension +// +// The override must stay inside the Crop component rather than being prepended +// to the tuple: prepending puts it above Shot type, so a performer with a +// face-tattoo close-up and a well-framed bust portrait would get the tattoo in +// every search dropdown +// +// Naming CROP_FACE in code is only possible because the taxonomy is fixed +func (v *Vocabulary) ThumbnailRank(types []models.ImageTypeEnum) image.RankTuple { + tuple := v.Rank(types) + + cropGroup, known := v.typeGroup[models.ImageTypeEnumCropFace] + if !known { + return tuple + } + + position, ranked := v.groupPosition[cropGroup] + if !ranked { + return tuple + } + + for _, imageType := range types { + if imageType == models.ImageTypeEnumCropFace { + tuple[position] = facePreference + break + } + } + + return tuple +} + +func (v *Vocabulary) ThumbnailRanksByImage(assignments []models.ImageTypeAssignment) map[uuid.UUID]image.RankTuple { + return v.ranksByImage(assignments, v.ThumbnailRank) +} diff --git a/internal/service/imagetype/rank_test.go b/internal/service/imagetype/rank_test.go new file mode 100644 index 000000000..1e80783ea --- /dev/null +++ b/internal/service/imagetype/rank_test.go @@ -0,0 +1,103 @@ +package imagetype + +import ( + "testing" + + "github.com/stashapp/stash-box/internal/image" + "github.com/stashapp/stash-box/internal/models" +) + +// A two-group vocabulary in the shape the loader produces: CROP first, POSE +// second, each type at its own position within its group +// +// Built directly rather than loaded, which is the point of testing here at all: +// the ranking is pure, and fetching it from the database would make the cheapest +// logic in the package the most expensive thing to check +func testVocabulary() *Vocabulary { + return &Vocabulary{ + groupPosition: map[string]int{"CROP": 0, "POSE": 1}, + groupCount: 2, + typePosition: map[models.ImageTypeEnum]int{ + models.ImageTypeEnumCropFace: 0, + models.ImageTypeEnumCropBust: 1, + models.ImageTypeEnumCropFullBody: 2, + models.ImageTypeEnumViewFront: 0, + models.ImageTypeEnumViewBack: 1, + }, + typeGroup: map[models.ImageTypeEnum]string{ + models.ImageTypeEnumCropFace: "CROP", + models.ImageTypeEnumCropBust: "CROP", + models.ImageTypeEnumCropFullBody: "CROP", + models.ImageTypeEnumViewFront: "POSE", + models.ImageTypeEnumViewBack: "POSE", + }, + } +} + +func equal(got, want image.RankTuple) bool { + if len(got) != len(want) { + return false + } + for i := range got { + if got[i] != want[i] { + return false + } + } + return true +} + +func TestRank(t *testing.T) { + v := testVocabulary() + + for _, tc := range []struct { + name string + types []models.ImageTypeEnum + want image.RankTuple + }{ + { + "nothing at all", + nil, + image.RankTuple{image.Unranked, image.Unranked}, + }, + { + "one group answered", + []models.ImageTypeEnum{models.ImageTypeEnumCropFace}, + image.RankTuple{0, image.Unranked}, + }, + { + "both groups answered", + []models.ImageTypeEnum{models.ImageTypeEnumCropBust, models.ImageTypeEnumViewBack}, + image.RankTuple{1, 1}, + }, + { + // The order the assignment query happens to return must not change + // the answer. FindImageTypesByPerformerIds orders by sort_order, so + // last-write-wins would have taken the lowest-priority type. + // Two-from-one-group is not reachable through the API, because + // every group is exclusive but that is a property of the seed + // data and of the validators, not of the schema, so the ranking + // has to behave when it happens anyway + "two from one group, best first", + []models.ImageTypeEnum{models.ImageTypeEnumCropFace, models.ImageTypeEnumCropFullBody}, + image.RankTuple{0, image.Unranked}, + }, + { + "two from one group, best last", + []models.ImageTypeEnum{models.ImageTypeEnumCropFullBody, models.ImageTypeEnumCropFace}, + image.RankTuple{0, image.Unranked}, + }, + { + // A type the instance has switched off is missing from typeGroup + // entirely, and simply stops counting + "a type outside the vocabulary", + []models.ImageTypeEnum{models.ImageTypeEnumDressNude, models.ImageTypeEnumViewFront}, + image.RankTuple{image.Unranked, 0}, + }, + } { + t.Run(tc.name, func(t *testing.T) { + if got := v.Rank(tc.types); !equal(got, tc.want) { + t.Errorf("Rank(%v) = %v, want %v", tc.types, got, tc.want) + } + }) + } +} diff --git a/internal/service/imagetype/service.go b/internal/service/imagetype/service.go new file mode 100644 index 000000000..290408649 --- /dev/null +++ b/internal/service/imagetype/service.go @@ -0,0 +1,198 @@ +// Package imagetype serves the image type vocabulary: a fixed set of labels, +// seeded by migration, that editors apply to an image's presence on an entity. +// Only sort_order is writable at runtime. +package imagetype + +import ( + "context" + "slices" + + "github.com/gofrs/uuid" + + "github.com/stashapp/stash-box/internal/models" + "github.com/stashapp/stash-box/internal/queries" + "github.com/stashapp/stash-box/internal/service/errutil" +) + +// ImageType handles image type vocabulary operations +type ImageType struct { + queries *queries.Queries + withTxn queries.WithTxnFunc +} + +// NewImageType creates a new image type service +func NewImageType(queries *queries.Queries, withTxn queries.WithTxnFunc) *ImageType { + return &ImageType{ + queries: queries, + withTxn: withTxn, + } +} + +// Groups returns the vocabulary as groups in priority order, each carrying its +// types in priority order. A non-nil target keeps only types valid for that +// entity kind, and drops any group thereby left empty +// +// Disabled entries are dropped unless includeDisabled, which only the admin +// screen passes: everywhere else asking for the vocabulary means asking what +// may be used, and a labeller offered a switched-off type makes no sense +func (s *ImageType) Groups(ctx context.Context, target *models.ImageTypeScopeEnum, includeDisabled bool) ([]models.ImageTypeGroup, error) { + dbGroups, err := s.queries.GetAllImageTypeGroups(ctx) + if err != nil { + return nil, err + } + + var dbTypes []queries.ImageType + if target != nil { + dbTypes, err = s.queries.GetImageTypesByTarget(ctx, string(*target)) + } else { + dbTypes, err = s.queries.GetAllImageTypes(ctx) + } + if err != nil { + return nil, err + } + + // Seeded one way round, served both, so a client need not know which side + // of a pair it is holding + dbConflicts, err := s.queries.GetAllImageTypeConflicts(ctx) + if err != nil { + return nil, err + } + + conflictsFor := make(map[string][]models.ImageTypeEnum, len(dbConflicts)*2) + for _, dbConflict := range dbConflicts { + conflictsFor[dbConflict.TypeKey] = append( + conflictsFor[dbConflict.TypeKey], models.ImageTypeEnum(dbConflict.ConflictsWithKey)) + conflictsFor[dbConflict.ConflictsWithKey] = append( + conflictsFor[dbConflict.ConflictsWithKey], models.ImageTypeEnum(dbConflict.TypeKey)) + } + + typesByGroup := make(map[string][]models.ImageType, len(dbGroups)) + for _, dbType := range dbTypes { + if !includeDisabled && !dbType.Enabled { + continue + } + typesByGroup[dbType.GroupKey] = append( + typesByGroup[dbType.GroupKey], typeToModel(dbType, conflictsFor[dbType.Key])) + } + + groups := make([]models.ImageTypeGroup, 0, len(dbGroups)) + for _, dbGroup := range dbGroups { + if !includeDisabled && !dbGroup.Enabled { + continue + } + + types := typesByGroup[dbGroup.Key] + + // A group with no type valid for the target would render as an empty + // section in the entity form, so omit it rather than return it bare. + // A group whose every type is switched off is the same case + if len(types) == 0 { + continue + } + + groups = append(groups, models.ImageTypeGroup{ + Key: models.ImageTypeGroupEnum(dbGroup.Key), + Name: dbGroup.Name, + Description: dbGroup.Description, + SortOrder: dbGroup.SortOrder, + Exclusive: dbGroup.Exclusive, + Enabled: dbGroup.Enabled, + Types: types, + }) + } + + return groups, nil +} + +// SetPerformerAssignments replaces a performer's type assignments wholesale. +// The performer_images rows must already exist: the composite foreign key +// requires them +func (s *ImageType) SetPerformerAssignments(ctx context.Context, performerID uuid.UUID, assignments []models.ImageTypeAssignment) error { + return s.withTxn(func(tx *queries.Queries) error { + if err := tx.DeletePerformerImageTypes(ctx, performerID); err != nil { + return err + } + + params := make([]queries.CreatePerformerImageTypesParams, len(assignments)) + for i, assignment := range assignments { + params[i] = queries.CreatePerformerImageTypesParams{ + PerformerID: performerID, + ImageID: assignment.ImageID, + TypeKey: string(assignment.Type), + } + } + + _, err := tx.CreatePerformerImageTypes(ctx, params) + return err + }) +} + +// LoadAssignmentsByPerformerIds returns each performer's type assignments, in +// vocabulary order, for the dataloader +func (s *ImageType) LoadAssignmentsByPerformerIds(ctx context.Context, ids []uuid.UUID) ([][]models.ImageTypeAssignment, []error) { + rows, err := s.queries.FindImageTypesByPerformerIds(ctx, ids) + if err != nil { + return nil, errutil.DuplicateError(err, len(ids)) + } + + byPerformer := make(map[uuid.UUID][]models.ImageTypeAssignment) + for _, row := range rows { + byPerformer[row.PerformerID] = append(byPerformer[row.PerformerID], models.ImageTypeAssignment{ + ImageID: row.ImageID, + Type: models.ImageTypeEnum(row.TypeKey), + }) + } + + result := make([][]models.ImageTypeAssignment, len(ids)) + for i, id := range ids { + result[i] = byPerformer[id] + } + return result, nil +} + +// LoadDatesByPerformerIds returns each performer's image dates, for the +// dataloader. Images without a date are included, carrying nil +func (s *ImageType) LoadDatesByPerformerIds(ctx context.Context, ids []uuid.UUID) ([][]models.ImageDate, []error) { + rows, err := s.queries.FindImageDatesByPerformerIds(ctx, ids) + if err != nil { + return nil, errutil.DuplicateError(err, len(ids)) + } + + byPerformer := make(map[uuid.UUID][]models.ImageDate) + for _, row := range rows { + byPerformer[row.PerformerID] = append(byPerformer[row.PerformerID], models.ImageDate{ + ImageID: row.ImageID, + Date: row.Date, + }) + } + + result := make([][]models.ImageDate, len(ids)) + for i, id := range ids { + result[i] = byPerformer[id] + } + return result, nil +} + +func typeToModel(dbType queries.ImageType, conflictsWith []models.ImageTypeEnum) models.ImageType { + validTypes := make([]models.ImageTypeScopeEnum, len(dbType.ValidTypes)) + for i, validType := range dbType.ValidTypes { + validTypes[i] = models.ImageTypeScopeEnum(validType) + } + + // Non-null in the schema, so an unconflicted type gets an empty list + // rather than a null a client would have to guard + if conflictsWith == nil { + conflictsWith = []models.ImageTypeEnum{} + } + slices.Sort(conflictsWith) + + return models.ImageType{ + Key: models.ImageTypeEnum(dbType.Key), + Name: dbType.Name, + Description: dbType.Description, + SortOrder: dbType.SortOrder, + ValidTypes: validTypes, + Enabled: dbType.Enabled, + ConflictsWith: conflictsWith, + } +} diff --git a/internal/service/imagetype/validate.go b/internal/service/imagetype/validate.go new file mode 100644 index 000000000..c0ba94879 --- /dev/null +++ b/internal/service/imagetype/validate.go @@ -0,0 +1,264 @@ +package imagetype + +import ( + "context" + "fmt" + "slices" + + "github.com/gofrs/uuid" + + "github.com/stashapp/stash-box/internal/models" + "github.com/stashapp/stash-box/internal/queries" +) + +// ValidateAssignments checks the constraints that hold however assignments are +// written: through an edit, or straight through performerCreate/performerUpdate. +// +// entityImageIDs is the entity's resulting image set: input.ImageIds on the +// direct path, the edit's resolved images on the edit path. assigned is what +// those images already carry, and is empty when the entity is being created. +func ValidateAssignments( + ctx context.Context, + q *queries.Queries, + target models.ImageTypeScopeEnum, + assignments []models.ImageAssignmentInput, + entityImageIDs []uuid.UUID, + assigned AssignedTypes, +) error { + if len(assignments) == 0 { + return nil + } + + // Checked before anything needing the vocabulary, so a date-only + // submission does not pay for a lookup it has no use for + for _, assignment := range assignments { + if err := validateImageDate(assignment.ImageID, assignment.Date); err != nil { + return err + } + } + + vocab, err := loadRules(ctx, q) + if err != nil { + return err + } + + onEntity := make(map[uuid.UUID]struct{}, len(entityImageIDs)) + for _, imageID := range entityImageIDs { + onEntity[imageID] = struct{}{} + } + + for _, assignment := range assignments { + if _, ok := onEntity[assignment.ImageID]; !ok { + return fmt.Errorf("image %s is not one of this entity's images", assignment.ImageID) + } + + for _, imageType := range assignment.Types { + key := string(imageType) + + if _, known := vocab.groupOf[key]; !known { + return fmt.Errorf("image type %s is not seeded", imageType) + } + + if !slices.Contains(vocab.validTargets[key], string(target)) { + return fmt.Errorf("image type %s cannot be applied to a %s", imageType, target) + } + + // Only newly-added types have to be enabled. Every save restates + // the labels an image already has, so rejecting those outright + // would make switching a type off strand every entity carrying it: + // unsaveable, with no way to drop the label except to turn the type + // back on. Switching a group off is meant to be reversible. + if !vocab.enabled[key] && !assigned.has(assignment.ImageID, imageType) { + return fmt.Errorf("image type %s is not enabled on this instance", imageType) + } + } + } + + return vocab.validateCombinations(assignments) +} + +// ValidateCombinations checks the constraints that merging can break, against +// an assignment set that no single submission ever stated +// +// Every edit is resolved against current state when applied, so two that were +// each valid when written can contradict each other by landing one after the +// other: E1 adding VIEW_FRONT and E2 adding VIEW_SIDE both validate against a +// clean image, and POSE allows one. Nothing in the schema catches it - +// performer_image_types has no per-group exclusion, and a partial unique index +// could not express conflicts_with anyway +// +// Only the combination rules are checked; the rest was settled when the edit +// was written and merging cannot break it +func ValidateCombinations(ctx context.Context, q *queries.Queries, assignments []models.ImageAssignmentInput) error { + if len(assignments) == 0 { + return nil + } + + vocab, err := loadRules(ctx, q) + if err != nil { + return err + } + + return vocab.validateCombinations(assignments) +} + +func (v rules) validateCombinations(assignments []models.ImageAssignmentInput) error { + seenImages := make(map[uuid.UUID]struct{}, len(assignments)) + + for _, assignment := range assignments { + if _, duplicate := seenImages[assignment.ImageID]; duplicate { + return fmt.Errorf("image %s appears more than once in image_types", assignment.ImageID) + } + seenImages[assignment.ImageID] = struct{}{} + + seenTypes := make(map[models.ImageTypeEnum]struct{}, len(assignment.Types)) + typeInGroup := make(map[string]models.ImageTypeEnum, len(assignment.Types)) + + for _, imageType := range assignment.Types { + if _, duplicate := seenTypes[imageType]; duplicate { + return fmt.Errorf("image %s lists %s more than once", assignment.ImageID, imageType) + } + seenTypes[imageType] = struct{}{} + + // Exclusivity prevents contradiction: an image cannot be both + // CROP_FACE and CROP_WIDE. It is a property of the group, and + // unrelated to whether many images may share a type + groupKey := v.groupOf[string(imageType)] + if other, used := typeInGroup[groupKey]; used && v.exclusive[groupKey] { + return fmt.Errorf("image %s cannot be both %s and %s: %s allows at most one", + assignment.ImageID, other, imageType, groupKey) + } + typeInGroup[groupKey] = imageType + } + + // Cross-group impossibilities, once the image's whole set is known. + // Exclusivity above is about one group contradicting itself; this is + // one group contradicting another - a face crop cannot be topless + // because the chest is not in the frame + if err := conflictBetween(assignment.ImageID, assignment.Types, v.conflicts); err != nil { + return err + } + } + + return nil +} + +// rules is the seeded reference data both checks read, loaded once. Distinct from +// Vocabulary in rank.go, which leaves disabled entries out entirely: ranking must +// ignore them, validation has to be able to reject them +type rules struct { + groupOf map[string]string + validTargets map[string][]string + enabled map[string]bool + exclusive map[string]bool + conflicts map[[2]models.ImageTypeEnum]struct{} +} + +func loadRules(ctx context.Context, q *queries.Queries) (rules, error) { + dbGroups, err := q.GetAllImageTypeGroups(ctx) + if err != nil { + return rules{}, err + } + + v := rules{ + exclusive: make(map[string]bool, len(dbGroups)), + } + + enabledGroups := make(map[string]bool, len(dbGroups)) + for _, dbGroup := range dbGroups { + v.exclusive[dbGroup.Key] = dbGroup.Exclusive + enabledGroups[dbGroup.Key] = dbGroup.Enabled + } + + dbTypes, err := q.GetAllImageTypes(ctx) + if err != nil { + return rules{}, err + } + + v.groupOf = make(map[string]string, len(dbTypes)) + v.validTargets = make(map[string][]string, len(dbTypes)) + v.enabled = make(map[string]bool, len(dbTypes)) + for _, dbType := range dbTypes { + v.groupOf[dbType.Key] = dbType.GroupKey + v.validTargets[dbType.Key] = dbType.ValidTypes + // A group being off disables its types by implication, so both are + // folded into one lookup rather than checked separately + v.enabled[dbType.Key] = dbType.Enabled && enabledGroups[dbType.GroupKey] + } + + dbConflicts, err := q.GetAllImageTypeConflicts(ctx) + if err != nil { + return rules{}, err + } + + v.conflicts = make(map[[2]models.ImageTypeEnum]struct{}, len(dbConflicts)) + for _, dbConflict := range dbConflicts { + v.conflicts[conflictKey( + models.ImageTypeEnum(dbConflict.TypeKey), + models.ImageTypeEnum(dbConflict.ConflictsWithKey), + )] = struct{}{} + } + + return v, nil +} + +// AssignedTypes is the set of types an entity's images already carry, keyed by +// image. A nil map is the create case and grandfathers nothing +type AssignedTypes map[uuid.UUID]map[models.ImageTypeEnum]struct{} + +func (a AssignedTypes) has(imageID uuid.UUID, imageType models.ImageTypeEnum) bool { + _, ok := a[imageID][imageType] + return ok +} + +// PerformerAssignedTypes reads what a performer's images already carry. The +// lookup lives at the call site rather than inside ValidateAssignments because +// the assignment tables are per-target and the validator is not +func PerformerAssignedTypes(ctx context.Context, q *queries.Queries, performerID uuid.UUID) (AssignedTypes, error) { + rows, err := q.FindImageTypesByPerformerIds(ctx, []uuid.UUID{performerID}) + if err != nil { + return nil, err + } + + assigned := make(AssignedTypes) + for _, row := range rows { + if assigned[row.ImageID] == nil { + assigned[row.ImageID] = make(map[models.ImageTypeEnum]struct{}) + } + assigned[row.ImageID][models.ImageTypeEnum(row.TypeKey)] = struct{}{} + } + + return assigned, nil +} + +// conflictKey pairs two types in a stable order, so a pair seeded one way +// round is found however the caller happens to list them +func conflictKey(a, b models.ImageTypeEnum) [2]models.ImageTypeEnum { + if a > b { + return [2]models.ImageTypeEnum{b, a} + } + return [2]models.ImageTypeEnum{a, b} +} + +func conflictBetween(imageID uuid.UUID, types []models.ImageTypeEnum, conflicts map[[2]models.ImageTypeEnum]struct{}) error { + for i, first := range types { + for _, second := range types[i+1:] { + if _, forbidden := conflicts[conflictKey(first, second)]; forbidden { + return fmt.Errorf("image %s cannot be both %s and %s", imageID, first, second) + } + } + } + return nil +} + +// validateImageDate accepts the three partial ISO 8601 precisions the schema +// already uses for uncertain dates, checked here because the column is text: +// nothing downstream would reject 2019-13 +func validateImageDate(imageID uuid.UUID, date *string) error { + if err := models.ValidateFuzzyString(date); err != nil { + return fmt.Errorf("image %s has an invalid date %q: expected YYYY, YYYY-MM or YYYY-MM-DD", + imageID, *date) + } + + return nil +} diff --git a/internal/service/performer/joins.go b/internal/service/performer/joins.go index 5f65c9c9d..76f134644 100644 --- a/internal/service/performer/joins.go +++ b/internal/service/performer/joins.go @@ -87,23 +87,127 @@ func updateURLs(ctx context.Context, tx *queries.Queries, performerID uuid.UUID, return createURLs(ctx, tx, performerID, urls) } -func createImages(ctx context.Context, tx *queries.Queries, performerID uuid.UUID, images []uuid.UUID) error { - var params []queries.CreatePerformerImagesParams +func createImages(ctx context.Context, tx *queries.Queries, performerID uuid.UUID, images []uuid.UUID, imageTypes []models.ImageAssignmentInput) error { + return writeImages(ctx, tx, performerID, images, imageTypes, nil, nil) +} + +// resolveDates works out each image's date after a write. Unlike labels, a +// date is single-valued, so an entry overrides rather than merges - including +// overriding with null to clear it. An image the submission does not touch +// keeps the date it had +func resolveDates(currentDates []queries.PerformerImage, imageTypes []models.ImageAssignmentInput) map[uuid.UUID]*string { + dates := make(map[uuid.UUID]*string, len(currentDates)) + for _, row := range currentDates { + dates[row.ImageID] = row.Date + } + + for _, entry := range imageTypes { + dates[entry.ImageID] = entry.Date + } + + return dates +} + +func updateImages(ctx context.Context, tx *queries.Queries, performerID uuid.UUID, images []uuid.UUID, imageTypes []models.ImageAssignmentInput) error { + // TODO Remove unused images + + // Read before the delete below, which cascades the assignments away with the + // join rows through performer_image_types' composite foreign key. Preserving + // them is active work, not the default + current, err := tx.FindImageTypesByPerformerIds(ctx, []uuid.UUID{performerID}) + if err != nil { + return err + } + + // Same for date, a column on those rows + currentDates, err := tx.FindImageDatesByPerformerIds(ctx, []uuid.UUID{performerID}) + if err != nil { + return err + } + + if err := tx.DeletePerformerImages(ctx, performerID); err != nil { + return err + } + + return writeImages(ctx, tx, performerID, images, imageTypes, current, currentDates) +} + +func writeImages(ctx context.Context, tx *queries.Queries, performerID uuid.UUID, images []uuid.UUID, imageTypes []models.ImageAssignmentInput, current []queries.PerformerImageType, currentDates []queries.PerformerImage) error { + dates := resolveDates(currentDates, imageTypes) + + // A repeated id would violate performer_images' primary key. The insert + // uses COPY, which admits no ON CONFLICT, so dedupe here instead + seen := make(map[uuid.UUID]struct{}, len(images)) + unique := make([]uuid.UUID, 0, len(images)) + + var imageParams []queries.CreatePerformerImagesParams for _, image := range images { - params = append(params, queries.CreatePerformerImagesParams{ + if _, duplicate := seen[image]; duplicate { + continue + } + seen[image] = struct{}{} + unique = append(unique, image) + + imageParams = append(imageParams, queries.CreatePerformerImagesParams{ PerformerID: performerID, ImageID: image, + Date: dates[image], }) } - _, err := tx.CreatePerformerImages(ctx, params) + if _, err := tx.CreatePerformerImages(ctx, imageParams); err != nil { + return err + } + + // After the join rows, which the composite foreign key requires to exist + assignments := resolveAssignments(current, imageTypes, unique) + + typeParams := make([]queries.CreatePerformerImageTypesParams, len(assignments)) + for i, assignment := range assignments { + typeParams[i] = queries.CreatePerformerImageTypesParams{ + PerformerID: performerID, + ImageID: assignment.ImageID, + TypeKey: string(assignment.Type), + } + } + + _, err := tx.CreatePerformerImageTypes(ctx, typeParams) return err } -func updateImages(ctx context.Context, tx *queries.Queries, performerID uuid.UUID, images []uuid.UUID) error { - // TODO Remove unused images - if err := tx.DeletePerformerImages(ctx, performerID); err != nil { - return err +// resolveAssignments works out which assignments should exist after a write. +// It implements the performerCreate/performerUpdate columns of the table on +// ImageAssignmentInput in graphql/schema/types/image_type.graphql; the edit +// path implements the same table in edit/performer.go and edit.sql, and +// nothing makes the three agree except that table. +// +// Assignments for images that did not survive are dropped either way: the +// composite foreign key would reject them +func resolveAssignments(current []queries.PerformerImageType, imageTypes []models.ImageAssignmentInput, images []uuid.UUID) []models.ImageTypeAssignment { + if imageTypes != nil && len(imageTypes) == 0 { + return nil + } + + typesByImage := make(map[uuid.UUID][]models.ImageTypeEnum, len(images)) + for _, row := range current { + typesByImage[row.ImageID] = append(typesByImage[row.ImageID], models.ImageTypeEnum(row.TypeKey)) + } + + for _, entry := range imageTypes { + typesByImage[entry.ImageID] = entry.Types } - return createImages(ctx, tx, performerID, images) + + // Walking images rather than the map is what drops an image that did not + // survive: one gathered above but absent here is never emitted + var assignments []models.ImageTypeAssignment + for _, image := range images { + for _, imageType := range typesByImage[image] { + assignments = append(assignments, models.ImageTypeAssignment{ + ImageID: image, + Type: imageType, + }) + } + } + + return assignments } diff --git a/internal/service/performer/joins_test.go b/internal/service/performer/joins_test.go new file mode 100644 index 000000000..3072da7f3 --- /dev/null +++ b/internal/service/performer/joins_test.go @@ -0,0 +1,164 @@ +package performer + +import ( + "slices" + "testing" + + "github.com/gofrs/uuid" + + "github.com/stashapp/stash-box/internal/models" + "github.com/stashapp/stash-box/internal/queries" +) + +// Three images in a performer's gallery. Fixed rather than random so a failure +// names the same image twice running +var ( + imageA = uuid.Must(uuid.FromString("00000000-0000-0000-0000-0000000000a1")) + imageB = uuid.Must(uuid.FromString("00000000-0000-0000-0000-0000000000b2")) + imageC = uuid.Must(uuid.FromString("00000000-0000-0000-0000-0000000000c3")) +) + +// What updateImages reads back before deleting the rows: A is a face crop, B is +// a front pose +func currentAssignments() []queries.PerformerImageType { + return []queries.PerformerImageType{ + {ImageID: imageA, TypeKey: "CROP_FACE"}, + {ImageID: imageB, TypeKey: "VIEW_FRONT"}, + } +} + +func assignment(image uuid.UUID, imageType models.ImageTypeEnum) models.ImageTypeAssignment { + return models.ImageTypeAssignment{ImageID: image, Type: imageType} +} + +// An entry states the whole of what is true about its image, so it replaces +// rather than merges but only for the images it names. Making it +// authoritative over everything in image_ids would force every client that +// touches image_ids to restate the full label set or destroy it +func TestResolveAssignmentsIsAuthoritativeOnlyOverTheImagesItNames(t *testing.T) { + got := resolveAssignments( + currentAssignments(), + []models.ImageAssignmentInput{ + {ImageID: imageA, Types: []models.ImageTypeEnum{models.ImageTypeEnumViewBack}}, + }, + []uuid.UUID{imageA, imageB}, + ) + + want := []models.ImageTypeAssignment{ + assignment(imageA, models.ImageTypeEnumViewBack), // replaced, not added to + assignment(imageB, models.ImageTypeEnumViewFront), // not named, untouched + } + if !slices.Equal(got, want) { + t.Errorf("resolveAssignments = %v, want %v", got, want) + } +} + +// The per-image version of clearing: naming an image with no types strips it, +// and says nothing about any other image +func TestResolveAssignmentsEntryWithNoTypesClearsOnlyThatImage(t *testing.T) { + got := resolveAssignments( + currentAssignments(), + []models.ImageAssignmentInput{{ImageID: imageA, Types: nil}}, + []uuid.UUID{imageA, imageB}, + ) + + want := []models.ImageTypeAssignment{assignment(imageB, models.ImageTypeEnumViewFront)} + if !slices.Equal(got, want) { + t.Errorf("resolveAssignments = %v, want %v", got, want) + } +} + +// performer_image_types' composite foreign key points at performer_images, so +// an assignment for an image that is no longer in the gallery is not merely +// pointless because it fails the insert and takes the whole transaction with it. +// Both directions: a label left over from before, and one the submission asks +// for on an image it did not include. +// +// Asserts the property, and there is now one mechanism behind it: the emitting +// loop walks the image list, so an image not in it is never emitted. That is +// also what TestResolveAssignmentsOrdersByTheImageList checks +func TestResolveAssignmentsDropsImagesThatDidNotSurvive(t *testing.T) { + got := resolveAssignments( + currentAssignments(), // B is labelled but no longer in the gallery + []models.ImageAssignmentInput{ + {ImageID: imageC, Types: []models.ImageTypeEnum{models.ImageTypeEnumCropBust}}, + }, + []uuid.UUID{imageA}, // ... and C was never in it + ) + + want := []models.ImageTypeAssignment{assignment(imageA, models.ImageTypeEnumCropFace)} + if !slices.Equal(got, want) { + t.Errorf("resolveAssignments = %v, want %v", got, want) + } +} + +// A client sending the same image twice is stating it twice; the later entry is +// the more recent statement. Worth pinning because the alternative (merging them) +// would silently make Types additive for repeats but replacing otherwise +func TestResolveAssignmentsTakesTheLastEntryForARepeatedImage(t *testing.T) { + got := resolveAssignments( + nil, + []models.ImageAssignmentInput{ + {ImageID: imageA, Types: []models.ImageTypeEnum{models.ImageTypeEnumCropFace}}, + {ImageID: imageA, Types: []models.ImageTypeEnum{models.ImageTypeEnumViewBack}}, + }, + []uuid.UUID{imageA}, + ) + + want := []models.ImageTypeAssignment{assignment(imageA, models.ImageTypeEnumViewBack)} + if !slices.Equal(got, want) { + t.Errorf("resolveAssignments = %v, want %v", got, want) + } +} + +// Assignments are gathered through a map, so the output order has to come from +// the image list rather than from map iteration: otherwise the rows inserted +// differ run to run for no reason, and anything comparing them is flaky +func TestResolveAssignmentsOrdersByTheImageList(t *testing.T) { + images := []uuid.UUID{imageB, imageA} + first := resolveAssignments(currentAssignments(), nil, images) + + want := []models.ImageTypeAssignment{ + assignment(imageB, models.ImageTypeEnumViewFront), + assignment(imageA, models.ImageTypeEnumCropFace), + } + if !slices.Equal(first, want) { + t.Errorf("resolveAssignments = %v, want %v", first, want) + } + + for range 20 { + if got := resolveAssignments(currentAssignments(), nil, images); !slices.Equal(got, first) { + t.Fatalf("order varies between calls: %v then %v", first, got) + } + } +} + +func date(value string) *string { return &value } + +// Renders a date for a failure message, distinguishing "no entry for this +// image" from "an entry saying the date is null" +func dateOf(dates map[uuid.UUID]*string, image uuid.UUID) string { + value, present := dates[image] + switch { + case !present: + return "<absent>" + case value == nil: + return "<null>" + default: + return *value + } +} + +func TestResolveDatesAnEntryWithoutADateClearsTheDate(t *testing.T) { + dates := resolveDates( + []queries.PerformerImage{{ImageID: imageA, Date: date("2019-06-15")}}, + []models.ImageAssignmentInput{ + // Relabelling only: date is left as nil + {ImageID: imageA, Types: []models.ImageTypeEnum{models.ImageTypeEnumViewBack}}, + }, + ) + + if got := dateOf(dates, imageA); got != "<null>" { + t.Errorf("image A's date = %s, want <null> - an entry states the whole of what is true", got) + } +} diff --git a/internal/service/performer/service.go b/internal/service/performer/service.go index c63bf3276..b7e7ad88e 100644 --- a/internal/service/performer/service.go +++ b/internal/service/performer/service.go @@ -12,6 +12,7 @@ import ( "github.com/stashapp/stash-box/internal/models" "github.com/stashapp/stash-box/internal/queries" "github.com/stashapp/stash-box/internal/service/errutil" + "github.com/stashapp/stash-box/internal/service/imagetype" ) // Performer handles performer-related operations @@ -361,7 +362,11 @@ func (s *Performer) Create(ctx context.Context, input models.PerformerCreateInpu return err } - return createImages(ctx, tx, id, input.ImageIds) + if err := imagetype.ValidateAssignments(ctx, tx, models.ImageTypeScopeEnumPerformer, input.ImageTypes, input.ImageIds, nil); err != nil { + return err + } + + return createImages(ctx, tx, id, input.ImageIds, input.ImageTypes) }) return performer, err @@ -406,7 +411,16 @@ func (s *Performer) Update(ctx context.Context, input models.PerformerUpdateInpu } // Update images - return updateImages(ctx, tx, performer.ID, input.ImageIds) + assigned, err := imagetype.PerformerAssignedTypes(ctx, tx, performer.ID) + if err != nil { + return err + } + + if err := imagetype.ValidateAssignments(ctx, tx, models.ImageTypeScopeEnumPerformer, input.ImageTypes, input.ImageIds, assigned); err != nil { + return err + } + + return updateImages(ctx, tx, performer.ID, input.ImageIds, input.ImageTypes) }) // Commit