Repository: IgnisDa/ryot (language: rust)

Issue:
Add support for Hardcoverapp metadata provider for books
Docs: https://docs.hardcover.app/


Discussion / hints:
Hi, I can contribute to this 
Sure! Do join the discord in case you need any help from my end.
Hi @achyu-dev. I explored their API documentation and have decided to work on this myself. It would be great if you pick up some other issue (#966 for example). Thanks!
yeah sure, no issues, I understand @IgnisDa 

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 3118292bbf..4c1375959e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4602,6 +4602,7 @@ dependencies = [
  "hashbag",
  "itertools 0.14.0",
  "media-models",
+ "nest_struct",
  "paginate",
  "rand 0.9.0-beta.0",
  "reqwest 0.12.12",
diff --git a/apps/frontend/app/routes/_dashboard.media.groups.$action.tsx b/apps/frontend/app/routes/_dashboard.media.groups.$action.tsx
index 7f0923f4ab..555ebc4eea 100644
--- a/apps/frontend/app/routes/_dashboard.media.groups.$action.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.groups.$action.tsx
@@ -19,7 +19,7 @@ import { useLoaderData, useNavigate } from "@remix-run/react";
 import {
 	EntityLot,
 	GraphqlSortOrder,
-	MediaLot,
+	type MediaLot,
 	MediaSource,
 	MetadataGroupSearchDocument,
 	type MetadataGroupSearchQuery,
@@ -50,9 +50,10 @@ import {
 import { BaseMediaDisplayItem } from "~/components/common";
 import { MetadataGroupDisplayItem } from "~/components/media";
 import { commaDelimitedString, pageQueryParam } from "~/lib/generals";
-import { useAppSearchParam } from "~/lib/hooks";
+import { useAppSearchParam, useCoreDetails } from "~/lib/hooks";
 import { useBulkEditCollection } from "~/lib/state/collection";
 import {
+	getCoreDetails,
 	getEnhancedCookieName,
 	redirectToFirstPageIfOnInvalidPage,
 	serverGqlService,
@@ -72,12 +73,6 @@ enum Action {
 	Search = "search",
 }
 
-const SEARCH_SOURCES_ALLOWED: Partial<Record<MediaSource, MediaLot>> = {
-	[MediaSource.Tmdb]: MediaLot.Movie,
-	[MediaSource.Igdb]: MediaLot.VideoGame,
-	[MediaSource.YoutubeMusic]: MediaLot.Music,
-};
-
 export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 	const { action } = zx.parseParams(params, { action: z.nativeEnum(Action) });
 	const cookieName = await getEnhancedCookieName(`groups.${action}`, request);
@@ -118,7 +113,10 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 			const urlParse = zx.parseQuery(request, {
 				source: z.nativeEnum(MediaSource).default(MediaSource.Tmdb),
 			});
-			const lot = SEARCH_SOURCES_ALLOWED[urlParse.source];
+			const coreDetails = await getCoreDetails();
+			const lot = coreDetails.metadataGroupSourceLotMappings.find(
+				(m) => m.source === urlParse.source,
+			)?.lot;
 			invariant(lot);
 			const { metadataGroupSearch } =
 				await serverGqlService.authenticatedRequest(
@@ -161,6 +159,7 @@ export const meta = ({ params }: MetaArgs<typeof loader>) => {
 
 export default function Page() {
 	const loaderData = useLoaderData<typeof loader>();
+	const coreDetails = useCoreDetails();
 	const [_, { setP }] = useAppSearchParam(loaderData.cookieName);
 	const navigate = useNavigate();
 	const [
@@ -234,12 +233,12 @@ export default function Page() {
 					{loaderData.action === Action.Search ? (
 						<>
 							<Select
-								data={Object.keys(SEARCH_SOURCES_ALLOWED).map((o) => ({
-									value: o.toString(),
-									label: startCase(o.toLowerCase()),
-								}))}
-								defaultValue={loaderData.search?.url.source}
 								onChange={(v) => setP("source", v)}
+								defaultValue={loaderData.search?.url.source}
+								data={coreDetails.metadataGroupSourceLotMappings.map((o) => ({
+									value: o.source.toString(),
+									label: startCase(o.source.toLowerCase()),
+								}))}
 							/>
 						</>
 					) : null}
diff --git a/apps/frontend/app/routes/_dashboard.media.item.$id._index.tsx b/apps/frontend/app/routes/_dashboard.media.item.$id._index.tsx
index e690874fe9..f33d1a78fb 100644
--- a/apps/frontend/app/routes/_dashboard.media.item.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.item.$id._index.tsx
@@ -483,6 +483,7 @@ export default function Page() {
 											.with(MediaSource.Tmdb, () => "tmdb.svg")
 											.with(MediaSource.Vndb, () => "vndb.ico")
 											.with(MediaSource.YoutubeMusic, () => "youtube-music.png")
+											.with(MediaSource.Hardcover, () => "hardcover.png")
 											.with(MediaSource.Custom, () => undefined)
 											.exhaustive()}`}
 									/>
@@ -492,15 +493,16 @@ export default function Page() {
 										)}
 										{match(loaderData.metadataDetails.source)
 											.with(
-												MediaSource.Anilist,
 												MediaSource.Igdb,
-												MediaSource.Listennotes,
 												MediaSource.Tmdb,
 												MediaSource.Vndb,
+												MediaSource.Anilist,
+												MediaSource.Listennotes,
 												() => "%",
 											)
 											.with(
 												MediaSource.Audible,
+												MediaSource.Hardcover,
 												MediaSource.GoogleBooks,
 												() => "/5",
 											)
diff --git a/apps/frontend/app/routes/_dashboard.media.people.$action.tsx b/apps/frontend/app/routes/_dashboard.media.people.$action.tsx
index ce15266b90..ecf3fe26bf 100644
--- a/apps/frontend/app/routes/_dashboard.media.people.$action.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.people.$action.tsx
@@ -49,7 +49,7 @@ import {
 import { BaseMediaDisplayItem } from "~/components/common";
 import { PersonDisplayItem } from "~/components/media";
 import { commaDelimitedString, pageQueryParam } from "~/lib/generals";
-import { useAppSearchParam } from "~/lib/hooks";
+import { useAppSearchParam, useCoreDetails } from "~/lib/hooks";
 import { useBulkEditCollection } from "~/lib/state/collection";
 import {
 	getEnhancedCookieName,
@@ -72,16 +72,12 @@ enum Action {
 	Search = "search",
 }
 
-const SEARCH_SOURCES_ALLOWED = [
-	MediaSource.Tmdb,
-	MediaSource.Anilist,
-	MediaSource.Vndb,
-	MediaSource.Openlibrary,
-	MediaSource.Audible,
-	MediaSource.MangaUpdates,
-	MediaSource.Igdb,
-	MediaSource.YoutubeMusic,
-] as const;
+const searchUrlSchema = z.object({
+	isTmdbCompany: zx.BoolAsString.optional(),
+	isAnilistStudio: zx.BoolAsString.optional(),
+	isHardcoverPublisher: zx.BoolAsString.optional(),
+	source: z.nativeEnum(MediaSource).default(MediaSource.Tmdb),
+});
 
 export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 	const { action } = zx.parseParams(params, { action: z.nativeEnum(Action) });
@@ -120,11 +116,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 			] as const;
 		})
 		.with(Action.Search, async () => {
-			const urlParse = zx.parseQuery(request, {
-				source: z.nativeEnum(MediaSource).default(MediaSource.Tmdb),
-				isTmdbCompany: zx.BoolAsString.optional(),
-				isAnilistStudio: zx.BoolAsString.optional(),
-			});
+			const urlParse = zx.parseQuery(request, searchUrlSchema);
 			const { peopleSearch } = await serverGqlService.authenticatedRequest(
 				request,
 				PeopleSearchDocument,
@@ -132,8 +124,9 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 					input: {
 						source: urlParse.source,
 						sourceSpecifics: {
-							isAnilistStudio: urlParse.isAnilistStudio,
 							isTmdbCompany: urlParse.isTmdbCompany,
+							isAnilistStudio: urlParse.isAnilistStudio,
+							isHardcoverPublisher: urlParse.isHardcoverPublisher,
 						},
 						search: { page: query[pageQueryParam], query: query.query },
 					},
@@ -169,6 +162,7 @@ export const meta = ({ params }: MetaArgs<typeof loader>) => {
 export default function Page() {
 	const loaderData = useLoaderData<typeof loader>();
 	const navigate = useNavigate();
+	const coreDetails = useCoreDetails();
 	const [_e, { setP }] = useAppSearchParam(loaderData.cookieName);
 	const [
 		filtersModalOpened,
@@ -241,29 +235,38 @@ export default function Page() {
 					{loaderData.action === Action.Search ? (
 						<>
 							<Select
-								data={SEARCH_SOURCES_ALLOWED.map((o) => ({
+								onChange={(v) => setP("source", v)}
+								defaultValue={loaderData.peopleSearch?.url.source}
+								data={coreDetails.peopleSearchSources.map((o) => ({
 									value: o.toString(),
 									label: startCase(o.toLowerCase()),
 								}))}
-								defaultValue={loaderData.peopleSearch?.url.source}
-								onChange={(v) => setP("source", v)}
 							/>
 							{loaderData.peopleSearch?.url.source === MediaSource.Tmdb ? (
 								<Checkbox
+									label="Company"
 									checked={loaderData.peopleSearch?.url.isTmdbCompany}
 									onChange={(e) =>
 										setP("isTmdbCompany", String(e.target.checked))
 									}
-									label="Company"
 								/>
 							) : null}
 							{loaderData.peopleSearch?.url.source === MediaSource.Anilist ? (
 								<Checkbox
+									label="Studio"
 									checked={loaderData.peopleSearch?.url.isAnilistStudio}
 									onChange={(e) =>
 										setP("isAnilistStudio", String(e.target.checked))
 									}
-									label="Studio"
+								/>
+							) : null}
+							{loaderData.peopleSearch?.url.source === MediaSource.Hardcover ? (
+								<Checkbox
+									label="Publisher"
+									checked={loaderData.peopleSearch?.url.isHardcoverPublisher}
+									onChange={(e) =>
+										setP("isHardcoverPublisher", String(e.target.checked))
+									}
 								/>
 							) : null}
 						</>
@@ -374,11 +377,9 @@ const PersonSearchItem = (props: {
 				if (loaderData.peopleSearch) {
 					setIsLoading(true);
 					const id = await commitPerson(
-						props.item.identifier,
-						loaderData.peopleSearch.url.source,
 						props.item.name,
-						loaderData.peopleSearch.url.isTmdbCompany,
-						loaderData.peopleSearch.url.isAnilistStudio,
+						props.item.identifier,
+						loaderData.peopleSearch.url,
 					);
 					setIsLoading(false);
 					return navigate($path("/media/people/item/:id", { id }));
@@ -389,18 +390,23 @@ const PersonSearchItem = (props: {
 };
 
 const commitPerson = async (
-	identifier: string,
-	source: MediaSource,
 	name: string,
-	isTmdbCompany?: boolean,
-	isAnilistStudio?: boolean,
+	identifier: string,
+	additionalData: z.infer<typeof searchUrlSchema>,
 ) => {
 	const data = new FormData();
 	data.append("identifier", identifier);
-	data.append("source", source);
+	data.append("source", additionalData.source);
 	if (name) data.append("name", name);
-	if (isTmdbCompany) data.append("isTmdbCompany", String(isTmdbCompany));
-	if (isAnilistStudio) data.append("isAnilistStudio", String(isAnilistStudio));
+	if (additionalData.isTmdbCompany)
+		data.append("isTmdbCompany", String(additionalData.isTmdbCompany));
+	if (additionalData.isAnilistStudio)
+		data.append("isAnilistStudio", String(additionalData.isAnilistStudio));
+	if (additionalData.isHardcoverPublisher)
+		data.append(
+			"isHardcoverPublisher",
+			String(additionalData.isHardcoverPublisher),
+		);
 	const resp = await fetch($path("/actions", { intent: "commitPerson" }), {
 		method: "POST",
 		body: data,
diff --git a/apps/frontend/app/routes/_dashboard.media.people.item.$id._index.tsx b/apps/frontend/app/routes/_dashboard.media.people.item.$id._index.tsx
index 62a3f3fd66..04fcfaeab3 100644
--- a/apps/frontend/app/routes/_dashboard.media.people.item.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.people.item.$id._index.tsx
@@ -27,6 +27,7 @@ import {
 	IconUser,
 } from "@tabler/icons-react";
 import { useQuery } from "@tanstack/react-query";
+import type { ReactNode } from "react";
 import { $path } from "remix-routes";
 import { useLocalStorage } from "usehooks-ts";
 import { z } from "zod";
@@ -97,6 +98,29 @@ export default function Page() {
 	const totalMetadataGroups = sum(
 		loaderData.personDetails.associatedMetadataGroups.map((c) => c.count),
 	);
+	const additionalPersonDetails = [
+		totalMetadata ? `${totalMetadata} media items` : null,
+		totalMetadataGroups ? `${totalMetadataGroups} groups` : null,
+		loaderData.personDetails.details.birthDate &&
+			`Birth: ${loaderData.personDetails.details.birthDate}`,
+		loaderData.personDetails.details.deathDate &&
+			`Death: ${loaderData.personDetails.details.deathDate}`,
+		loaderData.personDetails.details.place &&
+			loaderData.personDetails.details.place,
+		loaderData.personDetails.details.gender,
+		loaderData.personDetails.details.alternateNames &&
+			loaderData.personDetails.details.alternateNames.length > 0 &&
+			`Also called ${loaderData.personDetails.details.alternateNames.slice(0, 5).join(", ")}`,
+		loaderData.personDetails.details.website && (
+			<Anchor
+				target="_blank"
+				referrerPolicy="no-referrer"
+				href={loaderData.personDetails.details.website}
+			>
+				Website
+			</Anchor>
+		),
+	].filter(Boolean);
 
 	return (
 		<Container>
@@ -118,35 +142,13 @@ export default function Page() {
 							.then((data) => data.personDetails.details.isPartial),
 				}}
 			>
-				<Text c="dimmed" fz={{ base: "sm", lg: "md" }}>
-					{[
-						totalMetadata ? `${totalMetadata} media items` : null,
-						totalMetadataGroups ? `${totalMetadataGroups} groups` : null,
-						loaderData.personDetails.details.birthDate &&
-							`Birth: ${loaderData.personDetails.details.birthDate}`,
-						loaderData.personDetails.details.deathDate &&
-							`Death: ${loaderData.personDetails.details.deathDate}`,
-						loaderData.personDetails.details.place &&
-							loaderData.personDetails.details.place,
-						loaderData.personDetails.details.gender,
-					]
-						.filter(Boolean)
-						.join(" • ")}
-					{loaderData.personDetails.details.website ? (
-						<>
-							{" "}
-							•{" "}
-							<Anchor
-								target="_blank"
-								referrerPolicy="no-referrer"
-								fz={{ base: "xs", md: "sm" }}
-								href={loaderData.personDetails.details.website}
-							>
-								Website
-							</Anchor>
-						</>
-					) : null}
-				</Text>
+				{additionalPersonDetails.length > 0 ? (
+					<Text c="dimmed" fz={{ base: "sm", lg: "md" }}>
+						{additionalPersonDetails
+							.map<ReactNode>((s) => s)
+							.reduce((prev, curr) => [prev, " • ", curr])}
+					</Text>
+				) : null}
 				{loaderData.userPersonDetails.collections.length > 0 ? (
 					<Group>
 						{loaderData.userPersonDetails.collections.map((col) => (
diff --git a/apps/frontend/app/routes/actions.tsx b/apps/frontend/app/routes/actions.tsx
index b946ffee6f..da37824b9e 100644
--- a/apps/frontend/app/routes/actions.tsx
+++ b/apps/frontend/app/routes/actions.tsx
@@ -108,8 +108,9 @@ export const action = async ({ request }: ActionFunctionArgs) => {
 						name: submission.name,
 						source: submission.source,
 						sourceSpecifics: {
-							isAnilistStudio: submission.isAnilistStudio,
 							isTmdbCompany: submission.isTmdbCompany,
+							isAnilistStudio: submission.isAnilistStudio,
+							isHardcoverPublisher: submission.isHardcoverPublisher,
 						},
 					},
 				},
@@ -504,11 +505,12 @@ const commitMediaSchema = z.object({
 });
 
 const commitPersonSchema = z.object({
+	name: z.string(),
 	identifier: z.string(),
 	source: z.nativeEnum(MediaSource),
-	name: z.string(),
 	isTmdbCompany: zx.BoolAsString.optional(),
 	isAnilistStudio: zx.BoolAsString.optional(),
+	isHardcoverPublisher: zx.BoolAsString.optional(),
 });
 
 const reviewCommentSchema = z.object({
diff --git a/apps/frontend/public/provider-logos/hardcover.png b/apps/frontend/public/provider-logos/hardcover.png
new file mode 100644
index 0000000000..efd707a8c6
Binary files /dev/null and b/apps/frontend/public/provider-logos/hardcover.png differ
diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs
index aea705423f..fbd2f4c6c9 100644
--- a/crates/config/src/lib.rs
+++ b/crates/config/src/lib.rs
@@ -110,9 +110,19 @@ pub struct GoogleBooksConfig {
     pub pass_raw_query: bool,
 }
 
+#[derive(Debug, Serialize, Deserialize, Clone, Config)]
+#[config(rename_all = "snake_case", env_prefix = "BOOKS_HARDCOVER_")]
+pub struct HardcoverConfig {
+    /// The API key to be used.
+    pub api_key: String,
+}
+
 #[derive(Debug, Serialize, Deserialize, Clone, Config)]
 #[config(rename_all = "snake_case")]
 pub struct BookConfig {
+    /// Settings related to Hardcover.
+    #[setting(nested)]
+    pub hardcover: HardcoverConfig,
     /// Settings related to Openlibrary.
     #[setting(nested)]
     pub openlibrary: OpenlibraryConfig,
@@ -451,6 +461,7 @@ impl AppConfig {
         let gt = || "****".to_owned();
         let mut cl = self.clone();
         cl.anime_and_manga.mal.client_id = gt();
+        cl.books.hardcover.api_key = gt();
         cl.books.google_books.api_key = gt();
         cl.database.url = gt();
         cl.file_storage.s3_region = gt();
diff --git a/crates/migrations/src/lib.rs b/crates/migrations/src/lib.rs
index 70459f1218..c18de09255 100644
--- a/crates/migrations/src/lib.rs
+++ b/crates/migrations/src/lib.rs
@@ -62,6 +62,7 @@ mod m20241228_changes_for_issue_1130;
 mod m20241229_changes_for_issue_1155;
 mod m20250112_changes_for_issue_1174;
 mod m20250113_changes_for_issue_1169;
+mod m20250115_changes_for_issue_1110;
 
 pub use m20230410_create_metadata::Metadata as AliasedMetadata;
 pub use m20230413_create_person::Person as AliasedPerson;
@@ -145,6 +146,7 @@ impl MigratorTrait for Migrator {
             Box::new(m20241229_changes_for_issue_1155::Migration),
             Box::new(m20250112_changes_for_issue_1174::Migration),
             Box::new(m20250113_changes_for_issue_1169::Migration),
+            Box::new(m20250115_changes_for_issue_1110::Migration),
         ]
     }
 }
diff --git a/crates/migrations/src/m20230413_create_person.rs b/crates/migrations/src/m20230413_create_person.rs
index 61be1b428c..2ab0323896 100644
--- a/crates/migrations/src/m20230413_create_person.rs
+++ b/crates/migrations/src/m20230413_create_person.rs
@@ -29,6 +29,7 @@ pub enum Person {
     SourceSpecifics,
     StateChanges,
     SourceUrl,
+    AlternateNames,
 }
 
 #[derive(Iden)]
@@ -144,6 +145,7 @@ impl MigrationTrait for Migration {
                     .col(ColumnDef::new(Person::SourceSpecifics).json_binary())
                     .col(ColumnDef::new(Person::StateChanges).json_binary())
                     .col(ColumnDef::new(Person::SourceUrl).text())
+                    .col(ColumnDef::new(Person::AlternateNames).array(ColumnType::Text))
                     .to_owned(),
             )
             .await?;
diff --git a/crates/migrations/src/m20250115_changes_for_issue_1110.rs b/crates/migrations/src/m20250115_changes_for_issue_1110.rs
new file mode 100644
index 0000000000..25d8770e76
--- /dev/null
+++ b/crates/migrations/src/m20250115_changes_for_issue_1110.rs
@@ -0,0 +1,20 @@
+use sea_orm_migration::prelude::*;
+
+#[derive(DeriveMigrationName)]
+pub struct Migration;
+
+#[async_trait::async_trait]
+impl MigrationTrait for Migration {
+    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
+        let db = manager.get_connection();
+        if !manager.has_column("person", "alternate_names").await? {
+            db.execute_unprepared(r#"ALTER TABLE "person" ADD COLUMN "alternate_names" text[]"#)
+                .await?;
+        }
+        Ok(())
+    }
+
+    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
+        Ok(())
+    }
+}
diff --git a/crates/models/common/src/lib.rs b/crates/models/common/src/lib.rs
index a060553534..59d679cf58 100644
--- a/crates/models/common/src/lib.rs
+++ b/crates/models/common/src/lib.rs
@@ -288,6 +288,7 @@ pub struct MetadataGroupSearchInput {
 pub struct PersonSourceSpecifics {
     pub is_tmdb_company: Option<bool>,
     pub is_anilist_studio: Option<bool>,
+    pub is_hardcover_publisher: Option<bool>,
 }
 
 #[skip_serializing_none]
diff --git a/crates/models/database/src/person.rs b/crates/models/database/src/person.rs
index 4aa017b4a7..8d3776b8ed 100644
--- a/crates/models/database/src/person.rs
+++ b/crates/models/database/src/person.rs
@@ -16,28 +16,29 @@ use serde::{Deserialize, Serialize};
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
+    pub name: String,
     pub identifier: String,
     pub source: MediaSource,
+    pub place: Option<String>,
+    pub gender: Option<String>,
     pub created_on: DateTimeUtc,
-    pub last_updated_on: DateTimeUtc,
-    pub name: String,
+    pub website: Option<String>,
     pub is_partial: Option<bool>,
-    #[sea_orm(column_type = "Json")]
-    #[graphql(skip)]
-    pub images: Option<Vec<MetadataImage>>,
+    pub source_url: Option<String>,
     #[sea_orm(ignore)]
     pub display_images: Vec<String>,
     pub description: Option<String>,
-    pub gender: Option<String>,
+    pub last_updated_on: DateTimeUtc,
     pub birth_date: Option<NaiveDate>,
     pub death_date: Option<NaiveDate>,
-    pub place: Option<String>,
-    pub website: Option<String>,
-    pub source_url: Option<String>,
+    #[sea_orm(column_type = "Json")]
     #[graphql(skip)]
-    pub source_specifics: Option<PersonSourceSpecifics>,
+    pub images: Option<Vec<MetadataImage>>,
+    pub alternate_names: Option<Vec<String>>,
     #[graphql(skip)]
     pub state_changes: Option<PersonStateChanges>,
+    #[graphql(skip)]
+    pub source_specifics: Option<PersonSourceSpecifics>,
 }
 
 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
diff --git a/crates/models/dependent/src/lib.rs b/crates/models/dependent/src/lib.rs
index 813b9598e7..d1e1927db8 100644
--- a/crates/models/dependent/src/lib.rs
+++ b/crates/models/dependent/src/lib.rs
@@ -131,6 +131,7 @@ pub struct PersonDetails {
     pub images: Option<Vec<String>>,
     pub death_date: Option<NaiveDate>,
     pub birth_date: Option<NaiveDate>,
+    pub alternate_names: Option<Vec<String>>,
     pub related_metadata: Vec<MetadataPersonRelated>,
     pub source_specifics: Option<PersonSourceSpecifics>,
     pub related_metadata_groups: Vec<MetadataGroupPersonRelated>,
@@ -224,6 +225,13 @@ pub struct MetadataLotSourceMappings {
     pub sources: Vec<MediaSource>,
 }
 
+#[skip_serializing_none]
+#[derive(PartialEq, Eq, Clone, Debug, SimpleObject, Serialize, Deserialize)]
+pub struct MetadataGroupSourceLotMapping {
+    pub lot: MediaLot,
+    pub source: MediaSource,
+}
+
 #[skip_serializing_none]
 #[derive(PartialEq, Eq, Clone, Debug, SimpleObject, Serialize, Deserialize)]
 pub struct CoreDetails {
@@ -242,9 +250,11 @@ pub struct CoreDetails {
     pub file_storage_enabled: bool,
     pub is_server_key_validated: bool,
     pub backend_errors: Vec<BackendError>,
+    pub people_search_sources: Vec<MediaSource>,
     pub exercise_parameters: ExerciseParameters,
     pub metadata_lot_source_mappings: Vec<MetadataLotSourceMappings>,
     pub metadata_provider_languages: Vec<ProviderLanguageInformation>,
+    pub metadata_group_source_lot_mappings: Vec<MetadataGroupSourceLotMapping>,
 }
 
 #[derive(SimpleObject)]
diff --git a/crates/models/enum/src/lib.rs b/crates/models/enum/src/lib.rs
index e5d65be123..dbe042ac1b 100644
--- a/crates/models/enum/src/lib.rs
+++ b/crates/models/enum/src/lib.rs
@@ -73,6 +73,7 @@ pub enum MediaSource {
     Itunes,
     Anilist,
     Audible,
+    Hardcover,
     Listennotes,
     GoogleBooks,
     Openlibrary,
diff --git a/crates/models/media/src/lib.rs b/crates/models/media/src/lib.rs
index 4a160e43a0..06c0ab2ebf 100644
--- a/crates/models/media/src/lib.rs
+++ b/crates/models/media/src/lib.rs
@@ -74,6 +74,7 @@ pub struct AudioBookSpecifics {
 #[graphql(input_name = "BookSpecificsInput")]
 pub struct BookSpecifics {
     pub pages: Option<i32>,
+    pub is_compilation: Option<bool>,
 }
 
 #[skip_serializing_none]
diff --git a/crates/providers/Cargo.toml b/crates/providers/Cargo.toml
index b2b5de12e6..dde20962bb 100644
--- a/crates/providers/Cargo.toml
+++ b/crates/providers/Cargo.toml
@@ -21,6 +21,7 @@ graphql_client = { workspace = true }
 hashbag = { workspace = true }
 itertools = { workspace = true }
 media-models = { path = "../models/media" }
+nest_struct = { workspace = true }
 paginate = { workspace = true }
 rand = { workspace = true }
 reqwest = { workspace = true }
diff --git a/crates/providers/src/google_books.rs b/crates/providers/src/google_books.rs
index 66be3a2bcf..12fa46ea7d 100644
--- a/crates/providers/src/google_books.rs
+++ b/crates/providers/src/google_books.rs
@@ -223,6 +223,7 @@ impl GoogleBooksService {
             publish_year: item.published_date.and_then(|d| convert_date_to_year(&d)),
             book_specifics: Some(BookSpecifics {
                 pages: item.page_count,
+                ..Default::default()
             }),
             source_url: Some(format!(
                 "https://www.google.co.in/books/edition/{}/{}",
diff --git a/crates/providers/src/hardcover.rs b/crates/providers/src/hardcover.rs
new file mode 100644
index 0000000000..bcf727f022
--- /dev/null
+++ b/crates/providers/src/hardcover.rs
@@ -0,0 +1,642 @@
+use anyhow::Result;
+use application_utils::get_base_http_client;
+use async_trait::async_trait;
+use chrono::NaiveDate;
+use common_models::{PersonSourceSpecifics, SearchDetails};
+use common_utils::PAGE_SIZE;
+use database_models::metadata_group::MetadataGroupWithoutId;
+use dependent_models::{
+    MetadataGroupSearchResponse, MetadataPersonRelated, PeopleSearchResponse, PersonDetails,
+    SearchResults,
+};
+use enum_models::{MediaLot, MediaSource};
+use media_models::{
+    BookSpecifics, CommitMediaInput, MetadataDetails, MetadataGroupSearchItem,
+    MetadataImageForMediaDetails, MetadataSearchItem, PartialMetadataPerson,
+    PartialMetadataWithoutId, PeopleSearchItem, UniqueMediaIdentifier,
+};
+use nest_struct::nest_struct;
+use reqwest::{
+    header::{HeaderValue, AUTHORIZATION},
+    Client,
+};
+use rust_decimal::Decimal;
+use serde::Deserialize;
+use traits::MediaProvider;
+
+static URL: &str = "https://api.hardcover.app/v1/graphql";
+
+#[nest_struct]
+#[derive(Debug, Deserialize)]
+struct Response<T> {
+    data: T,
+}
+
+#[nest_struct]
+#[derive(Debug, Deserialize)]
+struct Search {
+    search: nest! {
+        results: nest! {
+            found: i32,
+            hits: Vec<nest! { document: Item<String> }>,
+        }
+    },
+}
+
+#[nest_struct]
+#[derive(Debug, Deserialize)]
+struct Editions {
+    editions: Vec<nest! { book_id: i64 }>,
+}
+
+#[derive(Debug, Deserialize)]
+struct BooksByPk {
+    books_by_pk: Item<i64>,
+}
+
+#[derive(Debug, Deserialize)]
+struct AuthorsByPk {
+    authors_by_pk: Item<i64>,
+}
+
+#[derive(Debug, Deserialize)]
+struct PublishersByPk {
+    publishers_by_pk: Item<i64>,
+}
+
+#[derive(Debug, Deserialize)]
+struct SeriesByPk {
+    series_by_pk: Item<i64>,
+}
+
+#[nest_struct]
+#[derive(Debug, Deserialize)]
+struct Item<TId> {
+    id: TId,
+    pages: Option<i32>,
+    bio: Option<String>,
+    name: Option<String>,
+    slug: Option<String>,
+    title: Option<String>,
+    rating: Option<Decimal>,
+    release_year: Option<i32>,
+    compilation: Option<bool>,
+    books_count: Option<usize>,
+    image: Option<ImageOrLink>,
+    description: Option<String>,
+    born_date: Option<NaiveDate>,
+    death_date: Option<NaiveDate>,
+    links: Option<Vec<ImageOrLink>>,
+    release_date: Option<NaiveDate>,
+    images: Option<Vec<ImageOrLink>>,
+    alternate_names: Option<Vec<String>>,
+    editions: Option<Vec<nest! { book: Option<Item<TId>> }>>,
+    recommendations: Option<Vec<nest! { item_book: Option<Item<TId>> }>>,
+    cached_tags: Option<
+        nest! {
+          #[serde(rename = "Genre")]
+          genre: Option<Vec<nest! { tag: String }>>
+        },
+    >,
+    contributions: Option<
+        Vec<
+            nest! {
+                author_id: Option<TId>,
+                contribution: Option<String>,
+                author: Option<nest! { name: String }>,
+                book: Option<nest! { id: TId, title: String }>,
+            },
+        >,
+    >,
+    book_series: Option<
+        Vec<
+            nest! {
+                book: Option<Item<TId>>,
+                series: Option<nest! { id: TId, name: String }>
+            },
+        >,
+    >,
+}
+
+#[nest_struct]
+#[derive(Debug, Deserialize)]
+struct ImageOrLink {
+    url: Option<String>,
+}
+
+async fn get_search_response(
+    query: &str,
+    page: i32,
+    query_type: &str,
+    client: &Client,
+) -> Result<SearchSearchResults> {
+    let body = format!(
+        r#"
+query {{
+  search(
+    page: {page}, per_page: {PAGE_SIZE},
+    query: "{query}", query_type: "{query_type}"
+  ) {{
+    results
+  }}
+}}
+    "#
+    );
+    let data = client
+        .post(URL)
+        .json(&serde_json::json!({"query": body}))
+        .send()
+        .await?
+        .json::<Response<Search>>()
+        .await?;
+    Ok(data.data.search.results)
+}
+
+fn query_type_from_specifics(source_specifics: &Option<PersonSourceSpecifics>) -> String {
+    match source_specifics {
+        Some(source_specifics) if source_specifics.is_hardcover_publisher.unwrap_or(false) => {
+            "publisher".to_owned()
+        }
+        _ => "author".to_owned(),
+    }
+}
+
+pub struct HardcoverService {
+    client: Client,
+}
+
+impl HardcoverService {
+    pub async fn new(config: &config::HardcoverConfig) -> Self {
+        let client = get_base_http_client(Some(vec![(
+            AUTHORIZATION,
+            HeaderValue::from_str(&config.api_key).unwrap(),
+        )]));
+        Self { client }
+    }
+}
+
+#[async_trait]
+impl MediaProvider for HardcoverService {
+    async fn metadata_details(&self, identifier: &str) -> Result<MetadataDetails> {
+        let body = format!(
+            r#"
+query {{
+  books_by_pk(id: {identifier}) {{
+    id
+    slug
+    pages
+    title
+    rating
+    compilation
+    description
+    cached_tags
+    release_date
+    release_year
+    image {{ url }}
+    images {{ url }}
+    book_series {{ series {{ id name }} }}
+    contributions {{ contribution author_id author {{ name }} }}
+    recommendations(
+      where: {{
+        subject_id: {{ _eq: {identifier} }},
+        subject_type: {{ _eq: "Book" }}, item_type: {{ _eq: "Book" }}
+      }}
+    ) {{ item_id }}
+  }}
+}}
+    "#
+        );
+        let data = self
+            .client
+            .post(URL)
+            .json(&serde_json::json!({"query": body}))
+            .send()
+            .await?
+            .json::<Response<BooksByPk>>()
+            .await
+            .unwrap();
+        let data = data.data.books_by_pk;
+        let mut images = vec![];
+        if let Some(i) = data.image {
+            if let Some(image) = i.url {
+                images.push(MetadataImageForMediaDetails { image });
+            }
+        }
+        for i in data.images.into_iter().flatten() {
+            if let Some(image) = i.url {
+                images.push(MetadataImageForMediaDetails { image });
+            }
+        }
+        let details = MetadataDetails {
+            url_images: images,
+            lot: MediaLot::Book,
+            title: data.title.unwrap(),
+            provider_rating: data.rating,
+            description: data.description,
+            source: MediaSource::Hardcover,
+            publish_date: data.release_date,
+            publish_year: data.release_year,
+            identifier: data.id.to_string(),
+            book_specifics: Some(BookSpecifics {
+                pages: data.pages,
+                is_compilation: data.compilation,
+            }),
+            source_url: data
+                .slug
+                .map(|s| format!("https://hardcover.app/books/{s}")),
+            suggestions: data
+                .recommendations
+                .unwrap_or_default()
+                .into_iter()
+                .flat_map(|i| {
+                    i.item_book.map(|b| PartialMetadataWithoutId {
+                        lot: MediaLot::Book,
+                        title: b.title.unwrap(),
+                        identifier: b.id.to_string(),
+                        source: MediaSource::Hardcover,
+                        ..Default::default()
+                    })
+                })
+                .collect(),
+            genres: data
+                .cached_tags
+                .into_iter()
+                .flat_map(|t| t.genre.unwrap_or_default().into_iter().map(|g| g.tag))
+                .collect(),
+            groups: data
+                .book_series
+                .into_iter()
+                .flatten()
+                .filter_map(|s| {
+                    s.series.map(|r| CommitMediaInput {
+                        name: r.name,
+                        unique: UniqueMediaIdentifier {
+                            lot: MediaLot::Book,
+                            identifier: r.id.to_string(),
+                            source: MediaSource::Hardcover,
+                        },
+                    })
+                })
+                .collect(),
+            people: data
+                .contributions
+                .unwrap_or_default()
+                .into_iter()
+                .filter_map(|a| {
+                    a.author.and_then(|ath| {
+                        a.author_id.map(|c| PartialMetadataPerson {
+                            name: ath.name,
+                            identifier: c.to_string(),
+                            source: MediaSource::Hardcover,
+                            role: a
+                                .contribution
+                                .unwrap_or_else(|| "Author".to_owned())
+                                .to_string(),
+                            ..Default::default()
+                        })
+                    })
+                })
+                .collect(),
+            ..Default::default()
+        };
+        Ok(details)
+    }
+
+    async fn metadata_search(
+        &self,
+        query: &str,
+        page: Option<i32>,
+        _display_nsfw: bool,
+    ) -> Result<SearchResults<MetadataSearchItem>> {
+        let page = page.unwrap_or(1);
+        let response = get_search_response(query, page, "book", &self.client).await?;
+        let items = response
+            .hits
+            .into_iter()
+            .map(|h| MetadataSearchItem {
+                identifier: h.document.id,
+                title: h.document.title.unwrap(),
+                publish_year: h.document.release_year,
+                image: h.document.image.and_then(|i| i.url),
+            })
+            .collect();
+        let resp = SearchResults {
+            items,
+            details: SearchDetails {
+                total: response.found,
+                next_page: if page < response.found / PAGE_SIZE {
+                    Some(page + 1)
+                } else {
+                    None
+                },
+            },
+        };
+        Ok(resp)
+    }
+
+    async fn metadata_group_details(
+        &self,
+        identifier: &str,
+    ) -> Result<(MetadataGroupWithoutId, Vec<PartialMetadataWithoutId>)> {
+        let body = format!(
+            r#"
+{{
+  series_by_pk(id: {identifier}) {{
+    id
+    name
+    slug
+    books_count
+    description
+    book_series(
+      order_by: {{ position: asc }}
+      where: {{
+        book: {{
+          book_status_id: {{ _eq: "1" }}, compilation: {{ _eq: false }},
+          default_physical_edition: {{ language_id: {{ _eq: 1 }} }}
+        }}
+      }}
+    ) {{
+      book {{ id title }}
+    }}
+  }}
+}}
+    "#
+        );
+        let data = self
+            .client
+            .post(URL)
+            .json(&serde_json::json!({"query": body}))
+            .send()
+            .await?
+            .json::<Response<SeriesByPk>>()
+            .await
+            .unwrap();
+        let data = data.data.series_by_pk;
+        let details = MetadataGroupWithoutId {
+            lot: MediaLot::Book,
+            title: data.name.unwrap(),
+            description: data.description,
+            source: MediaSource::Hardcover,
+            identifier: data.id.to_string(),
+            parts: data.books_count.unwrap_or_default().try_into().unwrap(),
+            source_url: data
+                .slug
+                .map(|s| format!("https://hardcover.app/series/{s}")),
+            ..Default::default()
+        };
+        let related = data
+            .book_series
+            .into_iter()
+            .flatten()
+            .filter_map(|s| {
+                s.book.map(|r| PartialMetadataWithoutId {
+                    lot: MediaLot::Book,
+                    title: r.title.unwrap(),
+                    identifier: r.id.to_string(),
+                    source: MediaSource::Hardcover,
+                    ..Default::default()
+                })
+            })
+            .collect();
+        Ok((details, related))
+    }
+
+    async fn metadata_group_search(
+        &self,
+        query: &str,
+        page: Option<i32>,
+        _display_nsfw: bool,
+    ) -> Result<MetadataGroupSearchResponse> {
+        let page = page.unwrap_or(1);
+        let response = get_search_response(query, page, "series", &self.client).await?;
+        let items = response
+            .hits
+            .into_iter()
+            .map(|h| MetadataGroupSearchItem {
+                identifier: h.document.id,
+                parts: h.document.books_count,
+                name: h.document.name.unwrap(),
+                image: h.document.image.and_then(|i| i.url),
+            })
+            .collect();
+        let resp = MetadataGroupSearchResponse {
+            details: SearchDetails {
+                total: response.found,
+                next_page: if page < response.found / PAGE_SIZE {
+                    Some(page + 1)
+                } else {
+                    None
+                },
+            },
+            items,
+        };
+        Ok(resp)
+    }
+
+    async fn person_details(
+        &self,
+        identifier: &str,
+        source_specifics: &Option<PersonSourceSpecifics>,
+    ) -> Result<PersonDetails> {
+        match query_type_from_specifics(source_specifics).as_str() {
+            "author" => {
+                let body = format!(
+                    r#"
+{{
+  authors_by_pk(id: {identifier}) {{
+    id
+    bio
+    name
+    slug
+    links
+    born_date
+    death_date
+    image {{ url }}
+    alternate_names
+    contributions {{ contribution book {{ id title }} }}
+  }}
+}}
+    "#
+                );
+                let data = self
+                    .client
+                    .post(URL)
+                    .json(&serde_json::json!({"query": body}))
+                    .send()
+                    .await?
+                    .json::<Response<AuthorsByPk>>()
+                    .await
+                    .unwrap();
+                let data = data.data.authors_by_pk;
+                let mut images = vec![];
+                if let Some(i) = data.image {
+                    if let Some(image) = i.url {
+                        images.push(image);
+                    }
+                }
+                let details = PersonDetails {
+                    images: Some(images),
+                    description: data.bio,
+                    name: data.name.unwrap(),
+                    birth_date: data.born_date,
+                    death_date: data.death_date,
+                    source: MediaSource::Hardcover,
+                    identifier: data.id.to_string(),
+                    alternate_names: data.alternate_names,
+                    source_specifics: source_specifics.clone(),
+                    source_url: data
+                        .slug
+                        .map(|s| format!("https://hardcover.app/authors/{s}")),
+                    website: data
+                        .links
+                        .unwrap_or_default()
+                        .into_iter()
+                        .find(|i| i.url.is_some())
+                        .and_then(|i| i.url),
+                    related_metadata: data
+                        .contributions
+                        .unwrap_or_default()
+                        .into_iter()
+                        .filter_map(|c| {
+                            c.book.map(|b| MetadataPersonRelated {
+                                role: "Author".to_owned(),
+                                metadata: PartialMetadataWithoutId {
+                                    title: b.title,
+                                    lot: MediaLot::Book,
+                                    identifier: b.id.to_string(),
+                                    source: MediaSource::Hardcover,
+                                    ..Default::default()
+                                },
+                                ..Default::default()
+                            })
+                        })
+                        .collect(),
+                    ..Default::default()
+                };
+                Ok(details)
+            }
+            "publisher" => {
+                let body = format!(
+                    r#"
+{{
+  publishers_by_pk(id: {identifier}) {{
+    id
+    name
+    slug
+    editions(limit: 10000) {{ book {{ id title }} }}
+  }}
+}}
+                "#
+                );
+                let data = self
+                    .client
+                    .post(URL)
+                    .json(&serde_json::json!({"query": body}))
+                    .send()
+                    .await?
+                    .json::<Response<PublishersByPk>>()
+                    .await
+                    .unwrap();
+                let data = data.data.publishers_by_pk;
+                let details = PersonDetails {
+                    name: data.name.unwrap(),
+                    source: MediaSource::Hardcover,
+                    identifier: data.id.to_string(),
+                    source_specifics: source_specifics.clone(),
+                    source_url: data
+                        .slug
+                        .map(|s| format!("https://hardcover.app/publishers/{s}")),
+                    related_metadata: data
+                        .editions
+                        .unwrap_or_default()
+                        .into_iter()
+                        .filter_map(|e| {
+                            e.book.map(|b| MetadataPersonRelated {
+                                role: "Publisher".to_owned(),
+                                metadata: PartialMetadataWithoutId {
+                                    lot: MediaLot::Book,
+                                    title: b.title.unwrap(),
+                                    identifier: b.id.to_string(),
+                                    source: MediaSource::Hardcover,
+                                    ..Default::default()
+                                },
+                                ..Default::default()
+                            })
+                        })
+                        .collect(),
+                    ..Default::default()
+                };
+                Ok(details)
+            }
+            _ => unreachable!(),
+        }
+    }
+
+    async fn people_search(
+        &self,
+        query: &str,
+        page: Option<i32>,
+        source_specifics: &Option<PersonSourceSpecifics>,
+        _display_nsfw: bool,
+    ) -> Result<PeopleSearchResponse> {
+        let page = page.unwrap_or(1);
+        let query_type = query_type_from_specifics(source_specifics);
+        let response = get_search_response(query, page, &query_type, &self.client).await?;
+        let items = response
+            .hits
+            .into_iter()
+            .map(|h| PeopleSearchItem {
+                identifier: h.document.id,
+                name: h.document.name.unwrap(),
+                image: h.document.image.and_then(|i| i.url),
+                ..Default::default()
+            })
+            .collect();
+        let resp = SearchResults {
+            items,
+            details: SearchDetails {
+                total: response.found,
+                next_page: if page < response.found / PAGE_SIZE {
+                    Some(page + 1)
+                } else {
+                    None
+                },
+            },
+        };
+        Ok(resp)
+    }
+}
+
+fn get_isbn_body(isbn_type: &str, isbn: &str) -> String {
+    format!(
+        r#"
+query {{
+  editions(where: {{ isbn_{isbn_type}: {{ _eq: "{isbn}" }} }}) {{
+    book_id
+  }}
+}}
+    "#
+    )
+}
+
+impl HardcoverService {
+    pub async fn id_from_isbn(&self, isbn: &str) -> Option<String> {
+        for isbn_type in ["10", "13"] {
+            let body = get_isbn_body(isbn_type, isbn);
+            let rsp = self
+                .client
+                .post(URL)
+                .json(&serde_json::json!({ "query": body }))
+                .send()
+                .await
+                .unwrap()
+                .json::<Response<Editions>>()
+                .await
+                .unwrap();
+            if let Some(edition) = rsp.data.editions.first() {
+                return Some(edition.book_id.to_string());
+            }
+        }
+        None
+    }
+}
diff --git a/crates/providers/src/lib.rs b/crates/providers/src/lib.rs
index a8f9282b89..e7ce034e86 100644
--- a/crates/providers/src/lib.rs
+++ b/crates/providers/src/lib.rs
@@ -1,6 +1,7 @@
 pub mod anilist;
 pub mod audible;
 pub mod google_books;
+pub mod hardcover;
 pub mod igdb;
 pub mod itunes;
 pub mod listennotes;
diff --git a/crates/providers/src/openlibrary.rs b/crates/providers/src/openlibrary.rs
index a1fe684306..e62671395f 100644
--- a/crates/providers/src/openlibrary.rs
+++ b/crates/providers/src/openlibrary.rs
@@ -61,7 +61,6 @@ struct BookSearchItem {
     publish_year: Option<i32>,
     author_names: Vec<String>,
     description: Option<String>,
-    book_specifics: BookSpecifics,
     publish_date: Option<NaiveDate>,
 }
 
@@ -455,6 +454,7 @@ impl MediaProvider for OpenlibraryService {
             )),
             book_specifics: Some(BookSpecifics {
                 pages: Some(num_pages),
+                ..Default::default()
             }),
             ..Default::default()
         })
@@ -500,9 +500,6 @@ impl MediaProvider for OpenlibraryService {
                     identifier: get_key(&d.key),
                     publish_year: d.first_publish_year,
                     author_names: d.author_name.unwrap_or_default(),
-                    book_specifics: BookSpecifics {
-                        pages: d.number_of_pages_median,
-                    },
                     ..Default::default()
                 }
             })
diff --git a/crates/services/importer/src/audiobookshelf.rs b/crates/services/importer/src/audiobookshelf.rs
index cf11f8d5ea..c27e5b74ff 100644
--- a/crates/services/importer/src/audiobookshelf.rs
+++ b/crates/services/importer/src/audiobookshelf.rs
@@ -15,7 +15,9 @@ use media_models::{
     DeployUrlAndKeyImportInput, ImportOrExportMetadataItem, ImportOrExportMetadataItemSeen,
     UniqueMediaIdentifier,
 };
-use providers::{google_books::GoogleBooksService, openlibrary::OpenlibraryService};
+use providers::{
+    google_books::GoogleBooksService, hardcover::HardcoverService, openlibrary::OpenlibraryService,
+};
 use reqwest::{
     header::{HeaderValue, AUTHORIZATION},
     Client,
@@ -28,6 +30,7 @@ use super::{ImportFailStep, ImportFailedItem};
 pub async fn import<F>(
     input: DeployUrlAndKeyImportInput,
     ss: &Arc<SupportingService>,
+    hardcover_service: &HardcoverService,
     google_books_service: &GoogleBooksService,
     open_library_service: &OpenlibraryService,
     commit_metadata: impl Fn(UniqueMediaIdentifier) -> F,
@@ -76,6 +79,7 @@ where
                 match &metadata.isbn {
                     Some(isbn) => match get_identifier_from_book_isbn(
                         isbn,
+                        hardcover_service,
                         google_books_service,
                         open_library_service,
                     )
diff --git a/crates/services/importer/src/goodreads.rs b/crates/services/importer/src/goodreads.rs
index 6ec20625cd..bd3e439101 100644
--- a/crates/services/importer/src/goodreads.rs
+++ b/crates/services/importer/src/goodreads.rs
@@ -11,7 +11,9 @@ use media_models::{
     DeployGenericCsvImportInput, ImportOrExportItemRating, ImportOrExportItemReview,
     ImportOrExportMetadataItem, ImportOrExportMetadataItemSeen,
 };
-use providers::{google_books::GoogleBooksService, openlibrary::OpenlibraryService};
+use providers::{
+    google_books::GoogleBooksService, hardcover::HardcoverService, openlibrary::OpenlibraryService,
+};
 use rust_decimal::Decimal;
 use rust_decimal_macros::dec;
 use serde::Deserialize;
@@ -38,6 +40,7 @@ struct Book {
 
 pub async fn import(
     input: DeployGenericCsvImportInput,
+    hardcover_service: &HardcoverService,
     google_books_service: &GoogleBooksService,
     open_library_service: &OpenlibraryService,
 ) -> Result<ImportResult> {
@@ -77,8 +80,13 @@ pub async fn import(
             });
             continue;
         }
-        let Some((identifier, source)) =
-            get_identifier_from_book_isbn(&isbn, google_books_service, open_library_service).await
+        let Some((identifier, source)) = get_identifier_from_book_isbn(
+            &isbn,
+            hardcover_service,
+            google_books_service,
+            open_library_service,
+        )
+        .await
         else {
             failed.push(ImportFailedItem {
                 lot: Some(lot),
diff --git a/crates/services/importer/src/lib.rs b/crates/services/importer/src/lib.rs
index 5d8290a2ea..4b6bea0dd1 100644
--- a/crates/services/importer/src/lib.rs
+++ b/crates/services/importer/src/lib.rs
@@ -11,7 +11,7 @@ use database_models::{
 };
 use dependent_utils::{
     commit_metadata, deploy_background_job, generate_exercise_id, get_google_books_service,
-    get_openlibrary_service, get_tmdb_non_media_service, process_import,
+    get_hardcover_service, get_openlibrary_service, get_tmdb_non_media_service, process_import,
 };
 use enum_models::ImportSource;
 use enum_models::{ExerciseLot, ExerciseSource};
@@ -94,6 +94,7 @@ impl ImporterService {
             ImportSource::Goodreads => {
                 goodreads::import(
                     input.generic_csv.unwrap(),
+                    &get_hardcover_service(&self.0.config).await.unwrap(),
                     &get_google_books_service(&self.0.config).await.unwrap(),
                     &get_openlibrary_service(&self.0.config).await.unwrap(),
                 )
@@ -104,6 +105,7 @@ impl ImporterService {
             ImportSource::Storygraph => {
                 storygraph::import(
                     input.generic_csv.unwrap(),
+                    &get_hardcover_service(&self.0.config).await.unwrap(),
                     &get_google_books_service(&self.0.config).await.unwrap(),
                     &get_openlibrary_service(&self.0.config).await.unwrap(),
                 )
@@ -113,6 +115,7 @@ impl ImporterService {
                 audiobookshelf::import(
                     input.url_and_key.unwrap(),
                     &self.0,
+                    &get_hardcover_service(&self.0.config).await.unwrap(),
                     &get_google_books_service(&self.0.config).await.unwrap(),
                     &get_openlibrary_service(&self.0.config).await.unwrap(),
                     |input| {
diff --git a/crates/services/importer/src/storygraph.rs b/crates/services/importer/src/storygraph.rs
index 092ea89789..6794b70544 100644
--- a/crates/services/importer/src/storygraph.rs
+++ b/crates/services/importer/src/storygraph.rs
@@ -11,7 +11,9 @@ use media_models::{
     DeployGenericCsvImportInput, ImportOrExportItemRating, ImportOrExportItemReview,
     ImportOrExportMetadataItemSeen,
 };
-use providers::{google_books::GoogleBooksService, openlibrary::OpenlibraryService};
+use providers::{
+    google_books::GoogleBooksService, hardcover::HardcoverService, openlibrary::OpenlibraryService,
+};
 use rust_decimal::Decimal;
 use rust_decimal_macros::dec;
 use serde::{Deserialize, Serialize};
@@ -50,6 +52,7 @@ struct History {
 
 pub async fn import(
     input: DeployGenericCsvImportInput,
+    hardcover_service: &HardcoverService,
     google_books_service: &GoogleBooksService,
     open_library_service: &OpenlibraryService,
 ) -> Result<ImportResult> {
@@ -88,8 +91,13 @@ pub async fn import(
             });
             continue;
         };
-        let Some((identifier, source)) =
-            get_identifier_from_book_isbn(&isbn, google_books_service, open_library_service).await
+        let Some((identifier, source)) = get_identifier_from_book_isbn(
+            &isbn,
+            hardcover_service,
+            google_books_service,
+            open_library_service,
+        )
+        .await
         else {
             failed.push(ImportFailedItem {
                 lot: Some(lot),
diff --git a/crates/services/integration/src/lib.rs b/crates/services/integration/src/lib.rs
index 90be09faee..a58ab62ddb 100644
--- a/crates/services/integration/src/lib.rs
+++ b/crates/services/integration/src/lib.rs
@@ -10,10 +10,12 @@ use database_models::{
 };
 use database_utils::{server_key_validation_guard, user_by_id};
 use dependent_models::{ImportCompletedItem, ImportResult};
-use dependent_utils::{commit_metadata, process_import};
+use dependent_utils::{
+    commit_metadata, get_google_books_service, get_hardcover_service, get_openlibrary_service,
+    process_import,
+};
 use enum_models::{EntityLot, IntegrationLot, IntegrationProvider, MediaLot};
 use media_models::{CommitMediaInput, SeenShowExtraInformation};
-use providers::{google_books::GoogleBooksService, openlibrary::OpenlibraryService};
 use rust_decimal_macros::dec;
 use sea_orm::{ActiveModelTrait, ActiveValue, ColumnTrait, EntityTrait, QueryFilter, QuerySelect};
 use supporting_service::SupportingService;
@@ -276,8 +278,9 @@ impl IntegrationService {
                         specifics.audiobookshelf_base_url.unwrap(),
                         specifics.audiobookshelf_token.unwrap(),
                         &self.0,
-                        &GoogleBooksService::new(&self.0.config.books.google_books).await,
-                        &OpenlibraryService::new(&self.0.config.books.openlibrary).await,
+                        &get_hardcover_service(&self.0.config).await.unwrap(),
+                        &get_google_books_service(&self.0.config).await.unwrap(),
+                        &get_openlibrary_service(&self.0.config).await.unwrap(),
                         |input| {
                             commit_metadata(
                                 CommitMediaInput {
@@ -364,7 +367,9 @@ impl IntegrationService {
                 IntegrationProvider::Audiobookshelf => {
                     yank::audiobookshelf::sync_to_owned_collection(
                         specifics.audiobookshelf_base_url.unwrap(),
-                        GoogleBooksService::new(&self.0.config.books.google_books).await,
+                        &get_hardcover_service(&self.0.config).await.unwrap(),
+                        &get_google_books_service(&self.0.config).await.unwrap(),
+                        &get_openlibrary_service(&self.0.config).await.unwrap(),
                     )
                     .await
                 }
diff --git a/crates/services/integration/src/yank/audiobookshelf.rs b/crates/services/integration/src/yank/audiobookshelf.rs
index 7d681363dc..0a2d6cface 100644
--- a/crates/services/integration/src/yank/audiobookshelf.rs
+++ b/crates/services/integration/src/yank/audiobookshelf.rs
@@ -12,7 +12,9 @@ use external_utils::audiobookshelf::get_updated_metadata;
 use media_models::{
     ImportOrExportMetadataItem, ImportOrExportMetadataItemSeen, UniqueMediaIdentifier,
 };
-use providers::{google_books::GoogleBooksService, openlibrary::OpenlibraryService};
+use providers::{
+    google_books::GoogleBooksService, hardcover::HardcoverService, openlibrary::OpenlibraryService,
+};
 use reqwest::{
     header::{HeaderValue, AUTHORIZATION},
     Client,
@@ -31,6 +33,7 @@ pub async fn yank_progress<F>(
     base_url: String,
     access_token: String,
     ss: &Arc<SupportingService>,
+    hardcover_service: &HardcoverService,
     google_books_service: &GoogleBooksService,
     open_library_service: &OpenlibraryService,
     commit_metadata: impl Fn(UniqueMediaIdentifier) -> F,
@@ -61,6 +64,7 @@ where
                 match &metadata.isbn {
                     Some(isbn) => match get_identifier_from_book_isbn(
                         isbn,
+                        hardcover_service,
                         google_books_service,
                         open_library_service,
                     )
@@ -176,7 +180,9 @@ where
 
 pub async fn sync_to_owned_collection(
     access_token: String,
-    isbn_service: GoogleBooksService,
+    hardcover_service: &HardcoverService,
+    google_books_service: &GoogleBooksService,
+    open_library_service: &OpenlibraryService,
 ) -> Result<ImportResult> {
     let client = get_http_client(&access_token);
 
@@ -203,8 +209,15 @@ pub async fn sync_to_owned_collection(
             let (identifier, lot, source) =
                 if Some("epub".to_string()) == item.media.as_ref().unwrap().ebook_format {
                     match &metadata.isbn {
-                        Some(isbn) => match isbn_service.id_from_isbn(isbn).await {
-                            Some(id) => (id, MediaLot::Book, MediaSource::GoogleBooks),
+                        Some(isbn) => match get_identifier_from_book_isbn(
+                            isbn,
+                            hardcover_service,
+                            google_books_service,
+                            open_library_service,
+                        )
+                        .await
+                        {
+                            Some(data) => (data.0, MediaLot::Book, data.1),
                             _ => continue,
                         },
                         _ => continue,
diff --git a/crates/services/integration/src/yank/youtube_music.rs b/crates/services/integration/src/yank/youtube_music.rs
index 6179f9af4a..08bf10f4af 100644
--- a/crates/services/integration/src/yank/youtube_music.rs
+++ b/crates/services/integration/src/yank/youtube_music.rs
@@ -59,7 +59,7 @@ pub async fn yank_progress(
         .collect::<HashMap<_, _>>();
     let items_in_cache = ss
         .cache_service
-        .get_values(cache_keys.iter().map(|(_id, key)| key.clone()).collect())
+        .get_values(cache_keys.values().cloned().collect())
         .await
         .unwrap_or_default();
     let mut result = ImportResult::default();
diff --git a/crates/services/miscellaneous/src/lib.rs b/crates/services/miscellaneous/src/lib.rs
index 1db41f46a4..8c4927fb99 100644
--- a/crates/services/miscellaneous/src/lib.rs
+++ b/crates/services/miscellaneous/src/lib.rs
@@ -49,8 +49,9 @@ use dependent_utils::{
     commit_person, create_notification_for_user, create_partial_metadata, create_user_notification,
     deploy_after_handle_media_seen_tasks, deploy_background_job, deploy_update_metadata_group_job,
     deploy_update_metadata_job, deploy_update_person_job, first_metadata_image_as_url,
-    get_entity_recently_consumed, get_metadata_provider, get_openlibrary_service,
-    get_tmdb_non_media_service, get_users_and_cte_monitoring_entity, get_users_monitoring_entity,
+    get_entity_recently_consumed, get_google_books_service, get_hardcover_service,
+    get_metadata_provider, get_openlibrary_service, get_tmdb_non_media_service,
+    get_users_and_cte_monitoring_entity, get_users_monitoring_entity,
     handle_after_media_seen_tasks, is_metadata_finished_by_user, metadata_groups_list,
     metadata_images_as_urls, metadata_list, people_list, post_review, progress_update,
     refresh_collection_to_entity_association, remove_entity_from_collection,
@@ -84,10 +85,9 @@ use migrations::{
 use nanoid::nanoid;
 use notification_service::send_notification;
 use providers::{
-    anilist::NonMediaAnilistService, audible::AudibleService, google_books::GoogleBooksService,
-    igdb::IgdbService, itunes::ITunesService, listennotes::ListennotesService,
-    mal::NonMediaMalService, manga_updates::MangaUpdatesService, vndb::VndbService,
-    youtube_music::YoutubeMusicService,
+    anilist::NonMediaAnilistService, audible::AudibleService, igdb::IgdbService,
+    itunes::ITunesService, listennotes::ListennotesService, mal::NonMediaMalService,
+    manga_updates::MangaUpdatesService, vndb::VndbService, youtube_music::YoutubeMusicService,
 };
 use rust_decimal::Decimal;
 use rust_decimal_macros::dec;
@@ -1419,14 +1419,13 @@ ORDER BY RANDOM() LIMIT 10;
         let err = || Err(Error::new("This source is not supported".to_owned()));
         let service: Provider = match source {
             MediaSource::YoutubeMusic => Box::new(YoutubeMusicService::new().await),
+            MediaSource::Hardcover => Box::new(get_hardcover_service(&self.0.config).await?),
             MediaSource::Vndb => Box::new(VndbService::new(&self.0.config.visual_novels).await),
             MediaSource::Openlibrary => Box::new(get_openlibrary_service(&self.0.config).await?),
             MediaSource::Itunes => {
                 Box::new(ITunesService::new(&self.0.config.podcasts.itunes).await)
             }
-            MediaSource::GoogleBooks => {
-                Box::new(GoogleBooksService::new(&self.0.config.books.google_books).await)
-            }
+            MediaSource::GoogleBooks => Box::new(get_google_books_service(&self.0.config).await?),
             MediaSource::Audible => {
                 Box::new(AudibleService::new(&self.0.config.audio_books.audible).await)
             }
@@ -2277,17 +2276,18 @@ ORDER BY RANDOM() LIMIT 10;
             .filter(|i: &Vec<MetadataImage>| !i.is_empty());
         let mut current_state_changes = person.clone().state_changes.unwrap_or_default();
         let mut to_update_person: person::ActiveModel = person.clone().into();
+        to_update_person.images = ActiveValue::Set(images);
+        to_update_person.is_partial = ActiveValue::Set(Some(false));
+        to_update_person.name = ActiveValue::Set(provider_person.name);
         to_update_person.last_updated_on = ActiveValue::Set(Utc::now());
-        to_update_person.description = ActiveValue::Set(provider_person.description);
-        to_update_person.gender = ActiveValue::Set(provider_person.gender);
-        to_update_person.birth_date = ActiveValue::Set(provider_person.birth_date);
-        to_update_person.death_date = ActiveValue::Set(provider_person.death_date);
         to_update_person.place = ActiveValue::Set(provider_person.place);
+        to_update_person.gender = ActiveValue::Set(provider_person.gender);
         to_update_person.website = ActiveValue::Set(provider_person.website);
         to_update_person.source_url = ActiveValue::Set(provider_person.source_url);
-        to_update_person.images = ActiveValue::Set(images);
-        to_update_person.is_partial = ActiveValue::Set(Some(false));
-        to_update_person.name = ActiveValue::Set(provider_person.name);
+        to_update_person.birth_date = ActiveValue::Set(provider_person.birth_date);
+        to_update_person.death_date = ActiveValue::Set(provider_person.death_date);
+        to_update_person.description = ActiveValue::Set(provider_person.description);
+        to_update_person.alternate_names = ActiveValue::Set(provider_person.alternate_names);
         for data in provider_person.related_metadata.clone() {
             let title = data.metadata.title.clone();
             let pm = create_partial_metadata(data.metadata, &self.0.db).await?;
diff --git a/crates/services/supporting/src/lib.rs b/crates/services/supporting/src/lib.rs
index 2ac2263985..2288cb6cf8 100644
--- a/crates/services/supporting/src/lib.rs
+++ b/crates/services/supporting/src/lib.rs
@@ -8,12 +8,13 @@ use chrono::{NaiveDate, TimeZone, Utc};
 use common_models::{ApplicationCacheKey, BackendError};
 use common_utils::{
     convert_naive_to_utc, ryot_log, COMPILATION_TIMESTAMP, EXERCISE_LOT_MAPPINGS,
-    MEDIA_LOT_MAPPINGS, PAGE_SIZE,
+    METADATA_GROUP_SOURCE_LOT_MAPPINGS, METADATA_LOT_MAPPINGS, PAGE_SIZE, PEOPLE_SEARCH_SOURCES,
 };
 use database_models::prelude::Exercise;
 use dependent_models::{
     ApplicationCacheValue, CoreDetails, ExerciseFilters, ExerciseParameters,
-    ExerciseParametersLotMapping, MetadataLotSourceMappings, ProviderLanguageInformation,
+    ExerciseParametersLotMapping, MetadataGroupSourceLotMapping, MetadataLotSourceMappings,
+    ProviderLanguageInformation,
 };
 use enum_models::{
     ExerciseEquipment, ExerciseForce, ExerciseLevel, ExerciseLot, ExerciseMechanic, ExerciseMuscle,
@@ -146,11 +147,19 @@ impl SupportingService {
             disable_telemetry: self.config.disable_telemetry,
             smtp_enabled: self.config.server.smtp.is_enabled(),
             signup_allowed: self.config.users.allow_registration,
+            people_search_sources: PEOPLE_SEARCH_SOURCES.to_vec(),
             local_auth_disabled: self.config.users.disable_local_auth,
             token_valid_for_days: self.config.users.token_valid_for_days,
             repository_link: "https://github.com/ignisda/ryot".to_owned(),
             is_server_key_validated: self.get_is_server_key_validated().await,
-            metadata_lot_source_mappings: MEDIA_LOT_MAPPINGS
+            metadata_group_source_lot_mappings: METADATA_GROUP_SOURCE_LOT_MAPPINGS
+                .iter()
+                .map(|(source, lot)| MetadataGroupSourceLotMapping {
+                    lot: *lot,
+                    source: *source,
+                })
+                .collect(),
+            metadata_lot_source_mappings: METADATA_LOT_MAPPINGS
                 .iter()
                 .map(|(lot, sources)| MetadataLotSourceMappings {
                     lot: *lot,
@@ -199,15 +208,16 @@ impl SupportingService {
                                 .collect(),
                             "en".to_owned(),
                         ),
-                        MediaSource::GoogleBooks
-                        | MediaSource::Listennotes
-                        | MediaSource::Openlibrary
+                        MediaSource::Mal
                         | MediaSource::Igdb
-                        | MediaSource::MangaUpdates
-                        | MediaSource::Anilist
-                        | MediaSource::Mal
+                        | MediaSource::Vndb
                         | MediaSource::Custom
-                        | MediaSource::Vndb => (vec!["us".to_owned()], "us".to_owned()),
+                        | MediaSource::Anilist
+                        | MediaSource::Hardcover
+                        | MediaSource::GoogleBooks
+                        | MediaSource::Listennotes
+                        | MediaSource::Openlibrary
+                        | MediaSource::MangaUpdates => (vec!["us".to_owned()], "us".to_owned()),
                     };
                     ProviderLanguageInformation {
                         source,
diff --git a/crates/utils/common/src/lib.rs b/crates/utils/common/src/lib.rs
index 31bba0a958..4fb965d4ed 100644
--- a/crates/utils/common/src/lib.rs
+++ b/crates/utils/common/src/lib.rs
@@ -49,11 +49,15 @@ pub const EXERCISE_LOT_MAPPINGS: &[(ExerciseLot, &[WorkoutSetPersonalBest])] = &
     ),
     (ExerciseLot::Reps, &[WorkoutSetPersonalBest::Reps]),
 ];
-pub const MEDIA_LOT_MAPPINGS: &[(MediaLot, &[MediaSource])] = &[
+pub const METADATA_LOT_MAPPINGS: &[(MediaLot, &[MediaSource])] = &[
     (MediaLot::AudioBook, &[MediaSource::Audible]),
     (
         MediaLot::Book,
-        &[MediaSource::Openlibrary, MediaSource::GoogleBooks],
+        &[
+            MediaSource::Openlibrary,
+            MediaSource::GoogleBooks,
+            MediaSource::Hardcover,
+        ],
     ),
     (
         MediaLot::Podcast,
@@ -75,6 +79,25 @@ pub const MEDIA_LOT_MAPPINGS: &[(MediaLot, &[MediaSource])] = &[
     (MediaLot::VisualNovel, &[MediaSource::Vndb]),
 ];
 
+pub const PEOPLE_SEARCH_SOURCES: [MediaSource; 9] = [
+    MediaSource::Tmdb,
+    MediaSource::Anilist,
+    MediaSource::Vndb,
+    MediaSource::Openlibrary,
+    MediaSource::Audible,
+    MediaSource::MangaUpdates,
+    MediaSource::Igdb,
+    MediaSource::YoutubeMusic,
+    MediaSource::Hardcover,
+];
+
+pub const METADATA_GROUP_SOURCE_LOT_MAPPINGS: &[(MediaSource, MediaLot)] = &[
+    (MediaSource::Tmdb, MediaLot::Movie),
+    (MediaSource::Igdb, MediaLot::VideoGame),
+    (MediaSource::Hardcover, MediaLot::Book),
+    (MediaSource::YoutubeMusic, MediaLot::Music),
+];
+
 pub fn get_first_and_last_day_of_month(year: i32, month: u32) -> (NaiveDate, NaiveDate) {
     let first_day = NaiveDate::from_ymd_opt(year, month, 1).unwrap();
     let last_day = NaiveDate::from_ymd_opt(year, month + 1, 1)
diff --git a/crates/utils/dependent/src/lib.rs b/crates/utils/dependent/src/lib.rs
index 4b5687af3c..6127adb9f5 100644
--- a/crates/utils/dependent/src/lib.rs
+++ b/crates/utils/dependent/src/lib.rs
@@ -64,6 +64,7 @@ use providers::{
     anilist::{AnilistAnimeService, AnilistMangaService},
     audible::AudibleService,
     google_books::GoogleBooksService,
+    hardcover::HardcoverService,
     igdb::IgdbService,
     itunes::ITunesService,
     listennotes::ListennotesService,
@@ -132,6 +133,10 @@ pub async fn get_google_books_service(config: &config::AppConfig) -> Result<Goog
     Ok(GoogleBooksService::new(&config.books.google_books).await)
 }
 
+pub async fn get_hardcover_service(config: &config::AppConfig) -> Result<HardcoverService> {
+    Ok(HardcoverService::new(&config.books.hardcover).await)
+}
+
 pub async fn get_tmdb_non_media_service(
     ss: &Arc<SupportingService>,
 ) -> Result<NonMediaTmdbService> {
@@ -146,6 +151,7 @@ pub async fn get_metadata_provider(
     let err = || Err(Error::new("This source is not supported".to_owned()));
     let service: Provider = match source {
         MediaSource::YoutubeMusic => Box::new(YoutubeMusicService::new().await),
+        MediaSource::Hardcover => Box::new(get_hardcover_service(&ss.config).await?),
         MediaSource::Vndb => Box::new(VndbService::new(&ss.config.visual_novels).await),
         MediaSource::Openlibrary => Box::new(get_openlibrary_service(&ss.config).await?),
         MediaSource::Itunes => Box::new(ITunesService::new(&ss.config.podcasts.itunes).await),
@@ -2623,18 +2629,20 @@ pub async fn add_entity_to_collection(
 
 pub async fn get_identifier_from_book_isbn(
     isbn: &str,
+    hardcover_service: &HardcoverService,
     google_books_service: &GoogleBooksService,
     open_library_service: &OpenlibraryService,
 ) -> Option<(String, MediaSource)> {
-    let mut identifier = None;
-    let mut source = MediaSource::GoogleBooks;
+    if let Some(id) = hardcover_service.id_from_isbn(isbn).await {
+        return Some((id, MediaSource::Hardcover));
+    }
     if let Some(id) = google_books_service.id_from_isbn(isbn).await {
-        identifier = Some(id);
-    } else if let Some(id) = open_library_service.id_from_isbn(isbn).await {
-        identifier = Some(id);
-        source = MediaSource::Openlibrary;
+        return Some((id, MediaSource::GoogleBooks));
+    }
+    if let Some(id) = open_library_service.id_from_isbn(isbn).await {
+        return Some((id, MediaSource::Openlibrary));
     }
-    identifier.map(|id| (id, source))
+    None
 }
 
 pub async fn expire_user_collections_list_cache(
diff --git a/docs/content/guides/books.md b/docs/content/guides/books.md
index 0533e76835..c8c55827ff 100644
--- a/docs/content/guides/books.md
+++ b/docs/content/guides/books.md
@@ -1,32 +1,45 @@
 # Books
 
-A guide about books integration for Ryot.
+The default provider that Ryot uses for book tracking is
+[Openlibrary](https://openlibrary.org). You might find it lacking in some cases, so Ryot
+also supports other providers.
 
-## Integration with Google Books
+!!! warning
 
-Ryot also supports tracking books via [Google Books](https://books.google.com). However,
-the API is heavily rate limited, so it is not possible to hardcode the API keys in the
-application (unlike the others).
+   Once you have followed any of the following steps, restart the server and delete the
+   application cache using [this guide](../configuration.md#delete-all-cache).
 
-You can follow the below steps to obtain your own API keys and enable book tracking with
-Google Books.
+## Hardcover
 
-### Steps
+Ryot supports tracking books via [Hardcover](https://hardcover.app). I prefer this method
+because the quality of data is much better than Openlibrary or Google Books. As of writing
+this guide, the API key expires after a year, so you will need to renew it.
+
+You can use the following steps to obtain your own API keys:
+
+1. Create a [Hardcover](https://hardcover.app) account.
+2. Go to the [API Access](https://hardcover.app/account/api) settings page and copy the
+   header.
+3. Set the environment variable as described in the [configuration](../configuration.md)
+   docs.
+   ```bash
+   BOOKS_HARDCOVER_API_KEY="Bearer eyJhbGciOiJIUzI1NiIsCJ9.5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36Pw5c"
+   ```
+
+## Google Books
+
+Ryot also supports tracking books via [Google Books](https://books.google.com). You can
+follow the below steps to obtain your own API keys and enable book tracking with Google
+Books.
 
 1. Create a [Google Cloud Platform](https://cloud.google.com) account. Open your [Google
    Cloud Platform Console](https://console.cloud.google.com).
-
 2. Use the default project or click on "Create a project" on the dashboard.
-
 3. Open the [APIs & Services Dashboard](https://console.cloud.google.com/apis/dashboard).
-
 4. Click on "Enable APIs and Services". Search for "Google Books API" and click on
    "Enable".
-
 5. Click on "Credentials" on the left sidebar. Click on "Create Credentials" and select
    "API key".
-
 6.  Click on "Create" and copy the API key.
-
 7. Set the `BOOKS_GOOGLE_BOOKS_API_KEY` environment variable as described in the
    [configuration](../configuration.md) docs.
diff --git a/docs/content/guides/video-games.md b/docs/content/guides/video-games.md
index 2095a3e33a..fba4d07714 100644
--- a/docs/content/guides/video-games.md
+++ b/docs/content/guides/video-games.md
@@ -13,18 +13,14 @@ You can follow the below steps to obtain your own API keys and enable video game
 ### Steps
 
 1. Create a [Twitch](https://twitch.tv) account.
-
 2. Open your [developer console](https://dev.twitch.tv/console).
-
 3. Click on "Register Your Application" on the dashboard.
-
 4. Fill up the details. Any name will suffice but it must be unique. Click on "Create"
    when you are done.
-
 5. You will be guided back to your application dashboard. Click on "Manage" for
    the application you just created.
-
 6. Generate a client secret. Copy the **Client ID** and **Client Secret**.
-
 7. Set the `VIDEO_GAMES_*` environment variables as described in the
    [configuration](../configuration.md#important-parameters) docs.
+8. Restart the server and delete the application cache using [this
+   guide](../configuration.md#delete-all-cache).
diff --git a/docs/includes/backend-config-schema.yaml b/docs/includes/backend-config-schema.yaml
index 9d69dabfb5..a6f5da21a5 100644
--- a/docs/includes/backend-config-schema.yaml
+++ b/docs/includes/backend-config-schema.yaml
@@ -35,6 +35,12 @@ books:
     # @envvar BOOKS_GOOGLE_BOOKS_PASS_RAW_QUERY
     pass_raw_query: false
 
+  # Settings related to Hardcover.
+  hardcover:
+    # The API key to be used.
+    # @envvar BOOKS_HARDCOVER_API_KEY
+    api_key: ""
+
   # Settings related to Openlibrary.
   openlibrary:
     # The image sizes to fetch from Openlibrary.
diff --git a/docs/includes/export-schema.ts b/docs/includes/export-schema.ts
index 255fb903d2..7e2a70df1a 100644
--- a/docs/includes/export-schema.ts
+++ b/docs/includes/export-schema.ts
@@ -135,7 +135,7 @@ export interface ImportOrExportMetadataItemSeen {
 }
 
 /** The different sources (or providers) from which data can be obtained from. */
-export type MediaSource = 'mal' | 'igdb' | 'tmdb' | 'vndb' | 'custom' | 'itunes' | 'anilist' | 'audible' | 'listennotes' | 'google_books' | 'openlibrary' | 'manga_updates' | 'youtube_music';
+export type MediaSource = 'mal' | 'igdb' | 'tmdb' | 'vndb' | 'custom' | 'itunes' | 'anilist' | 'audible' | 'hardcover' | 'listennotes' | 'google_books' | 'openlibrary' | 'manga_updates' | 'youtube_music';
 
 /** Details about a specific media item that needs to be imported or exported. */
 export interface ImportOrExportMetadataItem {
@@ -158,7 +158,7 @@ export interface ImportOrExportMetadataItem {
 	 * The source of media.
 	 *
 	 * @default 'custom'
-	 * @type {'mal' | 'igdb' | 'tmdb' | 'vndb' | 'custom' | 'itunes' | 'anilist' | 'audible' | 'listennotes' | 'google_books' | 'openlibrary' | 'manga_updates' | 'youtube_music'}
+	 * @type {'mal' | 'igdb' | 'tmdb' | 'vndb' | 'custom' | 'itunes' | 'anilist' | 'audible' | 'hardcover' | 'listennotes' | 'google_books' | 'openlibrary' | 'manga_updates' | 'youtube_music'}
 	 */
 	source: MediaSource;
 	/** An string to help identify it in the original source. */
@@ -184,7 +184,7 @@ export interface ImportOrExportMetadataGroupItem {
 	 * The source of media.
 	 *
 	 * @default 'custom'
-	 * @type {'mal' | 'igdb' | 'tmdb' | 'vndb' | 'custom' | 'itunes' | 'anilist' | 'audible' | 'listennotes' | 'google_books' | 'openlibrary' | 'manga_updates' | 'youtube_music'}
+	 * @type {'mal' | 'igdb' | 'tmdb' | 'vndb' | 'custom' | 'itunes' | 'anilist' | 'audible' | 'hardcover' | 'listennotes' | 'google_books' | 'openlibrary' | 'manga_updates' | 'youtube_music'}
 	 */
 	source: MediaSource;
 	/** Name of the group. */
@@ -193,6 +193,7 @@ export interface ImportOrExportMetadataGroupItem {
 
 export interface PersonSourceSpecifics {
 	is_anilist_studio: boolean | null;
+	is_hardcover_publisher: boolean | null;
 	is_tmdb_company: boolean | null;
 }
 
@@ -210,7 +211,7 @@ export interface ImportOrExportPersonItem {
 	 * The source of data.
 	 *
 	 * @default 'custom'
-	 * @type {'mal' | 'igdb' | 'tmdb' | 'vndb' | 'custom' | 'itunes' | 'anilist' | 'audible' | 'listennotes' | 'google_books' | 'openlibrary' | 'manga_updates' | 'youtube_music'}
+	 * @type {'mal' | 'igdb' | 'tmdb' | 'vndb' | 'custom' | 'itunes' | 'anilist' | 'audible' | 'hardcover' | 'listennotes' | 'google_books' | 'openlibrary' | 'manga_updates' | 'youtube_music'}
 	 */
 	source: MediaSource;
 	/** The source specific data. */
diff --git a/libs/generated/src/graphql/backend/gql.ts b/libs/generated/src/graphql/backend/gql.ts
index 7b15877050..1e5c7ad913 100644
--- a/libs/generated/src/graphql/backend/gql.ts
+++ b/libs/generated/src/graphql/backend/gql.ts
@@ -15,14 +15,14 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/
  */
 const documents = {
     "mutation RegisterUser($input: RegisterUserInput!) {\n  registerUser(input: $input) {\n    __typename\n    ... on RegisterError {\n      error\n    }\n    ... on StringIdObject {\n      id\n    }\n  }\n}\n\nmutation LoginUser($input: AuthUserInput!) {\n  loginUser(input: $input) {\n    __typename\n    ... on LoginError {\n      error\n    }\n    ... on LoginResponse {\n      apiKey\n    }\n  }\n}\n\nmutation AddEntityToCollection($input: ChangeCollectionToEntityInput!) {\n  addEntityToCollection(input: $input)\n}\n\nmutation CommitMetadata($input: CommitMediaInput!) {\n  commitMetadata(input: $input) {\n    id\n  }\n}\n\nmutation CommitMetadataGroup($input: CommitMediaInput!) {\n  commitMetadataGroup(input: $input) {\n    id\n  }\n}\n\nmutation CommitPerson($input: CommitPersonInput!) {\n  commitPerson(input: $input) {\n    id\n  }\n}\n\nmutation CreateCustomExercise($input: ExerciseInput!) {\n  createCustomExercise(input: $input)\n}\n\nmutation UpdateCustomExercise($input: UpdateCustomExerciseInput!) {\n  updateCustomExercise(input: $input)\n}\n\nmutation UpdateUserIntegration($input: UpdateUserIntegrationInput!) {\n  updateUserIntegration(input: $input)\n}\n\nmutation CreateCustomMetadata($input: CreateCustomMetadataInput!) {\n  createCustomMetadata(input: $input) {\n    id\n  }\n}\n\nmutation UpdateCustomMetadata($input: UpdateCustomMetadataInput!) {\n  updateCustomMetadata(input: $input)\n}\n\nmutation CreateOrUpdateCollection($input: CreateOrUpdateCollectionInput!) {\n  createOrUpdateCollection(input: $input) {\n    id\n  }\n}\n\nmutation CreateReviewComment($input: CreateReviewCommentInput!) {\n  createReviewComment(input: $input)\n}\n\nmutation CreateUserMeasurement($input: UserMeasurementInput!) {\n  createUserMeasurement(input: $input)\n}\n\nmutation CreateUserNotificationPlatform($input: CreateUserNotificationPlatformInput!) {\n  createUserNotificationPlatform(input: $input)\n}\n\nmutation CreateUserIntegration($input: CreateUserIntegrationInput!) {\n  createUserIntegration(input: $input) {\n    id\n  }\n}\n\nmutation CreateOrUpdateUserWorkout($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkout(input: $input)\n}\n\nmutation CreateOrUpdateUserWorkoutTemplate($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkoutTemplate(input: $input)\n}\n\nmutation DeleteCollection($collectionName: String!) {\n  deleteCollection(collectionName: $collectionName)\n}\n\nmutation DeleteReview($reviewId: String!) {\n  deleteReview(reviewId: $reviewId)\n}\n\nmutation DeleteS3Object($key: String!) {\n  deleteS3Object(key: $key)\n}\n\nmutation DeleteSeenItem($seenId: String!) {\n  deleteSeenItem(seenId: $seenId) {\n    id\n  }\n}\n\nmutation DeleteUser($toDeleteUserId: String!) {\n  deleteUser(toDeleteUserId: $toDeleteUserId)\n}\n\nmutation DeleteUserIntegration($integrationId: String!) {\n  deleteUserIntegration(integrationId: $integrationId)\n}\n\nmutation DeleteUserMeasurement($timestamp: DateTime!) {\n  deleteUserMeasurement(timestamp: $timestamp)\n}\n\nmutation DeleteUserNotificationPlatform($notificationId: String!) {\n  deleteUserNotificationPlatform(notificationId: $notificationId)\n}\n\nmutation DeleteUserWorkout($workoutId: String!) {\n  deleteUserWorkout(workoutId: $workoutId)\n}\n\nmutation DeleteUserWorkoutTemplate($workoutTemplateId: String!) {\n  deleteUserWorkoutTemplate(workoutTemplateId: $workoutTemplateId)\n}\n\nmutation DeployBackgroundJob($jobName: BackgroundJob!) {\n  deployBackgroundJob(jobName: $jobName)\n}\n\nmutation DeployBulkProgressUpdate($input: [ProgressUpdateInput!]!) {\n  deployBulkProgressUpdate(input: $input)\n}\n\nmutation DeployExportJob {\n  deployExportJob\n}\n\nmutation DeployImportJob($input: DeployImportJobInput!) {\n  deployImportJob(input: $input)\n}\n\nmutation DeployUpdateMetadataJob($metadataId: String!) {\n  deployUpdateMetadataJob(metadataId: $metadataId)\n}\n\nmutation DeployUpdatePersonJob($personId: String!) {\n  deployUpdatePersonJob(personId: $personId)\n}\n\nmutation DeployUpdateMetadataGroupJob($metadataGroupId: String!) {\n  deployUpdateMetadataGroupJob(metadataGroupId: $metadataGroupId)\n}\n\nmutation UpdateSeenItem($input: UpdateSeenItemInput!) {\n  updateSeenItem(input: $input)\n}\n\nmutation UpdateUserNotificationPlatform($input: UpdateUserNotificationPlatformInput!) {\n  updateUserNotificationPlatform(input: $input)\n}\n\nmutation UpdateUserWorkoutAttributes($input: UpdateUserWorkoutAttributesInput!) {\n  updateUserWorkoutAttributes(input: $input)\n}\n\nmutation GenerateAuthToken {\n  generateAuthToken\n}\n\nmutation MergeMetadata($mergeFrom: String!, $mergeInto: String!) {\n  mergeMetadata(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation DisassociateMetadata($metadataId: String!) {\n  disassociateMetadata(metadataId: $metadataId)\n}\n\nmutation CreateOrUpdateReview($input: CreateOrUpdateReviewInput!) {\n  createOrUpdateReview(input: $input) {\n    id\n  }\n}\n\nmutation PresignedPutS3Url($input: PresignedPutUrlInput!) {\n  presignedPutS3Url(input: $input) {\n    key\n    uploadUrl\n  }\n}\n\nmutation RemoveEntityFromCollection($input: ChangeCollectionToEntityInput!) {\n  removeEntityFromCollection(input: $input) {\n    id\n  }\n}\n\nmutation TestUserNotificationPlatforms {\n  testUserNotificationPlatforms\n}\n\nmutation UpdateUser($input: UpdateUserInput!) {\n  updateUser(input: $input) {\n    id\n  }\n}\n\nmutation UpdateUserPreference($input: UserPreferencesInput!) {\n  updateUserPreference(input: $input)\n}\n\nmutation CreateAccessLink($input: CreateAccessLinkInput!) {\n  createAccessLink(input: $input) {\n    id\n  }\n}\n\nmutation ProcessAccessLink($input: ProcessAccessLinkInput!) {\n  processAccessLink(input: $input) {\n    __typename\n    ... on ProcessAccessLinkError {\n      error\n    }\n    ... on ProcessAccessLinkResponse {\n      apiKey\n      redirectTo\n      tokenValidForDays\n    }\n  }\n}\n\nmutation RevokeAccessLink($accessLinkId: String!) {\n  revokeAccessLink(accessLinkId: $accessLinkId)\n}\n\nmutation UpdateUserExerciseSettings($input: UpdateUserExerciseSettings!) {\n  updateUserExerciseSettings(input: $input)\n}\n\nmutation MergeExercise($mergeFrom: String!, $mergeInto: String!) {\n  mergeExercise(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation RefreshUserMetadataRecommendations {\n  refreshUserMetadataRecommendations\n}\n\nmutation MarkEntityAsPartial($input: MarkEntityAsPartialInput!) {\n  markEntityAsPartial(input: $input)\n}": types.RegisterUserDocument,
-    "query MetadataDetails($metadataId: String!) {\n  metadataDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    source\n    isNsfw\n    isPartial\n    sourceUrl\n    identifier\n    description\n    suggestions\n    publishYear\n    publishDate\n    providerRating\n    createdByUserId\n    productionStatus\n    originalLanguage\n    genres {\n      id\n      name\n    }\n    group {\n      id\n      name\n      part\n    }\n    assets {\n      images\n      videos {\n        videoId\n        source\n      }\n    }\n    creators {\n      name\n      items {\n        id\n        name\n        image\n        character\n      }\n    }\n    watchProviders {\n      name\n      image\n      languages\n    }\n    animeSpecifics {\n      episodes\n    }\n    audioBookSpecifics {\n      runtime\n    }\n    bookSpecifics {\n      pages\n    }\n    movieSpecifics {\n      runtime\n    }\n    mangaSpecifics {\n      volumes\n      chapters\n    }\n    podcastSpecifics {\n      episodes {\n        id\n        title\n        overview\n        thumbnail\n        number\n        runtime\n        publishDate\n      }\n      totalEpisodes\n    }\n    showSpecifics {\n      totalSeasons\n      totalEpisodes\n      runtime\n      seasons {\n        id\n        seasonNumber\n        name\n        overview\n        backdropImages\n        posterImages\n        episodes {\n          id\n          name\n          posterImages\n          episodeNumber\n          publishDate\n          name\n          overview\n          runtime\n        }\n      }\n    }\n    visualNovelSpecifics {\n      length\n    }\n    videoGameSpecifics {\n      platforms\n    }\n    musicSpecifics {\n      duration\n      viewCount\n      byVariousArtists\n    }\n  }\n}": types.MetadataDetailsDocument,
-    "query PersonDetails($personId: String!) {\n  personDetails(personId: $personId) {\n    associatedMetadata {\n      ...PersonDetailsGroupedByRolePart\n    }\n    associatedMetadataGroups {\n      ...PersonDetailsGroupedByRolePart\n    }\n    details {\n      id\n      name\n      place\n      source\n      gender\n      website\n      deathDate\n      birthDate\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}": types.PersonDetailsDocument,
+    "query MetadataDetails($metadataId: String!) {\n  metadataDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    source\n    isNsfw\n    isPartial\n    sourceUrl\n    identifier\n    description\n    suggestions\n    publishYear\n    publishDate\n    providerRating\n    createdByUserId\n    productionStatus\n    originalLanguage\n    animeSpecifics {\n      episodes\n    }\n    audioBookSpecifics {\n      runtime\n    }\n    movieSpecifics {\n      runtime\n    }\n    genres {\n      id\n      name\n    }\n    group {\n      id\n      name\n      part\n    }\n    watchProviders {\n      name\n      image\n      languages\n    }\n    bookSpecifics {\n      pages\n      isCompilation\n    }\n    mangaSpecifics {\n      volumes\n      chapters\n    }\n    assets {\n      images\n      videos {\n        videoId\n        source\n      }\n    }\n    creators {\n      name\n      items {\n        id\n        name\n        image\n        character\n      }\n    }\n    podcastSpecifics {\n      episodes {\n        id\n        title\n        overview\n        thumbnail\n        number\n        runtime\n        publishDate\n      }\n      totalEpisodes\n    }\n    showSpecifics {\n      totalSeasons\n      totalEpisodes\n      runtime\n      seasons {\n        id\n        seasonNumber\n        name\n        overview\n        backdropImages\n        posterImages\n        episodes {\n          id\n          name\n          posterImages\n          episodeNumber\n          publishDate\n          name\n          overview\n          runtime\n        }\n      }\n    }\n    visualNovelSpecifics {\n      length\n    }\n    videoGameSpecifics {\n      platforms\n    }\n    musicSpecifics {\n      duration\n      viewCount\n      byVariousArtists\n    }\n  }\n}": types.MetadataDetailsDocument,
+    "query PersonDetails($personId: String!) {\n  personDetails(personId: $personId) {\n    associatedMetadata {\n      ...PersonDetailsGroupedByRolePart\n    }\n    associatedMetadataGroups {\n      ...PersonDetailsGroupedByRolePart\n    }\n    details {\n      id\n      name\n      place\n      source\n      gender\n      website\n      deathDate\n      birthDate\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n      alternateNames\n    }\n  }\n}": types.PersonDetailsDocument,
     "query UserAnalytics($input: UserAnalyticsInput!) {\n  userAnalytics(input: $input) {\n    hours {\n      hour\n      entities {\n        entityId\n        entityLot\n        metadataLot\n      }\n    }\n    activities {\n      groupedBy\n      totalCount\n      totalDuration\n      items {\n        day\n        bookCount\n        showCount\n        totalCount\n        movieCount\n        musicCount\n        animeCount\n        mangaCount\n        workoutCount\n        podcastCount\n        totalDuration\n        totalBookPages\n        audioBookCount\n        videoGameCount\n        totalReviewCount\n        visualNovelCount\n        totalWorkoutReps\n        totalShowDuration\n        totalMovieDuration\n        totalMusicDuration\n        totalMetadataCount\n        totalWorkoutWeight\n        totalPodcastDuration\n        totalWorkoutDistance\n        totalWorkoutRestTime\n        totalWorkoutDuration\n        userMeasurementCount\n        totalVideoGameDuration\n        totalAudioBookDuration\n        totalPersonReviewCount\n        totalVisualNovelDuration\n        totalMetadataReviewCount\n        totalWorkoutPersonalBests\n        totalCollectionReviewCount\n        totalMetadataGroupReviewCount\n      }\n    }\n    fitness {\n      workoutReps\n      workoutCount\n      workoutWeight\n      workoutDistance\n      workoutDuration\n      workoutRestTime\n      measurementCount\n      workoutPersonalBests\n      workoutExercises {\n        count\n        exercise\n      }\n      workoutMuscles {\n        count\n        muscle\n      }\n      workoutEquipments {\n        count\n        equipment\n      }\n    }\n  }\n}": types.UserAnalyticsDocument,
     "query UserDetails {\n  userDetails {\n    __typename\n    ... on User {\n      id\n      lot\n      name\n      isDisabled\n      oidcIssuerId\n      preferences {\n        general {\n          reviewScale\n          gridPacking\n          displayNsfw\n          disableVideos\n          persistQueries\n          disableReviews\n          disableIntegrations\n          disableWatchProviders\n          disableNavigationAnimation\n          dashboard {\n            hidden\n            section\n            numElements\n            deduplicateMedia\n          }\n          watchProviders {\n            lot\n            values\n          }\n        }\n        fitness {\n          logging {\n            muteSounds\n            promptForRestTimer\n            showDetailsWhileEditing\n          }\n          exercises {\n            unitSystem\n            setRestTimers {\n              ...SetRestTimersPart\n            }\n          }\n          measurements {\n            custom {\n              name\n              dataType\n            }\n            inbuilt {\n              weight\n              bodyMassIndex\n              totalBodyWater\n              muscle\n              leanBodyMass\n              bodyFat\n              boneMass\n              visceralFat\n              waistCircumference\n              waistToHeightRatio\n              hipCircumference\n              waistToHipRatio\n              chestCircumference\n              thighCircumference\n              bicepsCircumference\n              neckCircumference\n              bodyFatCaliper\n              chestSkinfold\n              abdominalSkinfold\n              thighSkinfold\n              basalMetabolicRate\n              totalDailyEnergyExpenditure\n              calories\n            }\n          }\n        }\n        notifications {\n          toSend\n          enabled\n        }\n        featuresEnabled {\n          analytics {\n            enabled\n          }\n          others {\n            calendar\n            collections\n          }\n          fitness {\n            enabled\n            workouts\n            templates\n            measurements\n          }\n          media {\n            enabled\n            groups\n            people\n            genres\n            specific\n          }\n        }\n      }\n    }\n  }\n}": types.UserDetailsDocument,
     "query UserExerciseDetails($exerciseId: String!) {\n  userExerciseDetails(exerciseId: $exerciseId) {\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    history {\n      idx\n      workoutId\n      workoutEndOn\n      bestSet {\n        ...WorkoutSetRecordPart\n      }\n    }\n    details {\n      exerciseId\n      createdOn\n      lastUpdatedOn\n      exerciseNumTimesInteracted\n      exerciseExtraInformation {\n        settings {\n          excludeFromAnalytics\n          setRestTimers {\n            ...SetRestTimersPart\n          }\n        }\n        lifetimeStats {\n          weight\n          reps\n          distance\n          duration\n          personalBestsAchieved\n        }\n        personalBests {\n          lot\n          sets {\n            setIdx\n            workoutId\n            exerciseIdx\n          }\n        }\n      }\n    }\n  }\n}": types.UserExerciseDetailsDocument,
     "query UserMeasurementsList($input: UserMeasurementsListInput!) {\n  userMeasurementsList(input: $input) {\n    timestamp\n    name\n    comment\n    stats {\n      weight\n      bodyMassIndex\n      totalBodyWater\n      muscle\n      leanBodyMass\n      bodyFat\n      boneMass\n      visceralFat\n      waistCircumference\n      waistToHeightRatio\n      hipCircumference\n      waistToHipRatio\n      chestCircumference\n      thighCircumference\n      bicepsCircumference\n      neckCircumference\n      bodyFatCaliper\n      chestSkinfold\n      abdominalSkinfold\n      thighSkinfold\n      basalMetabolicRate\n      totalDailyEnergyExpenditure\n      calories\n      custom\n    }\n  }\n}": types.UserMeasurementsListDocument,
     "query UserMetadataDetails($metadataId: String!) {\n  userMetadataDetails(metadataId: $metadataId) {\n    mediaReason\n    hasInteracted\n    averageRating\n    seenByAllCount\n    seenByUserCount\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    history {\n      ...SeenPart\n    }\n    nextEntry {\n      season\n      volume\n      episode\n      chapter\n    }\n    inProgress {\n      ...SeenPart\n    }\n    collections {\n      ...CollectionPart\n    }\n    showProgress {\n      timesSeen\n      seasonNumber\n      episodes {\n        episodeNumber\n        timesSeen\n      }\n    }\n    podcastProgress {\n      episodeNumber\n      timesSeen\n    }\n  }\n}": types.UserMetadataDetailsDocument,
-    "query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    key\n    size\n    endedAt\n    startedAt\n  }\n}\n\nquery UserCollectionsList($name: String) {\n  userCollectionsList(name: $name) {\n    id\n    name\n    count\n    isDefault\n    description\n    creator {\n      id\n      name\n    }\n    collaborators {\n      id\n      name\n    }\n    informationTemplate {\n      lot\n      name\n      required\n      description\n      defaultValue\n    }\n  }\n}\n\nquery UserIntegrations {\n  userIntegrations {\n    id\n    lot\n    provider\n    createdOn\n    isDisabled\n    maximumProgress\n    minimumProgress\n    lastTriggeredOn\n    syncToOwnedCollection\n  }\n}\n\nquery UserNotificationPlatforms {\n  userNotificationPlatforms {\n    id\n    lot\n    createdOn\n    isDisabled\n    description\n  }\n}\n\nquery UsersList($query: String) {\n  usersList(query: $query) {\n    id\n    lot\n    name\n    isDisabled\n  }\n}\n\nquery UserMetadataRecommendations {\n  userMetadataRecommendations\n}\n\nquery UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {\n  userUpcomingCalendarEvents(input: $input) {\n    ...CalendarEventPart\n  }\n}\n\nquery UserCalendarEvents($input: UserCalendarEventInput!) {\n  userCalendarEvents(input: $input) {\n    date\n    events {\n      ...CalendarEventPart\n    }\n  }\n}\n\nquery MetadataPartialDetails($metadataId: String!) {\n  metadataPartialDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    image\n    publishYear\n  }\n}\n\nquery MetadataGroupsList($input: MetadataGroupsListInput!) {\n  metadataGroupsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery PeopleList($input: PeopleListInput!) {\n  peopleList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAccessLinks {\n  userAccessLinks {\n    id\n    name\n    isDemo\n    createdOn\n    expiresOn\n    timesUsed\n    isRevoked\n    maximumUses\n    isAccountDefault\n    isMutationAllowed\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    name\n    level\n    force\n    source\n    muscles\n    mechanic\n    equipment\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery GenreDetails($input: GenreDetailsInput!) {\n  genreDetails(input: $input) {\n    details {\n      id\n      name\n      numItems\n    }\n    contents {\n      details {\n        total\n        nextPage\n      }\n      items\n    }\n  }\n}\n\nquery CollectionContents($input: CollectionContentsInput!) {\n  collectionContents(input: $input) {\n    user {\n      id\n      name\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    results {\n      details {\n        total\n        nextPage\n      }\n      items {\n        entityId\n        entityLot\n      }\n    }\n    details {\n      name\n      description\n      createdOn\n    }\n  }\n}\n\nquery CoreDetails {\n  coreDetails {\n    version\n    docsLink\n    pageSize\n    websiteUrl\n    smtpEnabled\n    oidcEnabled\n    signupAllowed\n    repositoryLink\n    disableTelemetry\n    tokenValidForDays\n    localAuthDisabled\n    fileStorageEnabled\n    isServerKeyValidated\n    metadataLotSourceMappings {\n      lot\n      sources\n    }\n    metadataProviderLanguages {\n      source\n      default\n      supported\n    }\n    frontend {\n      url\n      oidcButtonLabel\n      dashboardMessage\n      umami {\n        domains\n        scriptUrl\n        websiteId\n      }\n    }\n    exerciseParameters {\n      downloadRequired\n      filters {\n        type\n        level\n        force\n        mechanic\n        equipment\n        muscle\n      }\n      lotMapping {\n        lot\n        bests\n      }\n    }\n  }\n}\n\nquery MetadataGroupDetails($metadataGroupId: String!) {\n  metadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    contents\n    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}": types.GetOidcRedirectUrlDocument,
+    "query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    key\n    size\n    endedAt\n    startedAt\n  }\n}\n\nquery UserCollectionsList($name: String) {\n  userCollectionsList(name: $name) {\n    id\n    name\n    count\n    isDefault\n    description\n    creator {\n      id\n      name\n    }\n    collaborators {\n      id\n      name\n    }\n    informationTemplate {\n      lot\n      name\n      required\n      description\n      defaultValue\n    }\n  }\n}\n\nquery UserIntegrations {\n  userIntegrations {\n    id\n    lot\n    provider\n    createdOn\n    isDisabled\n    maximumProgress\n    minimumProgress\n    lastTriggeredOn\n    syncToOwnedCollection\n  }\n}\n\nquery UserNotificationPlatforms {\n  userNotificationPlatforms {\n    id\n    lot\n    createdOn\n    isDisabled\n    description\n  }\n}\n\nquery UsersList($query: String) {\n  usersList(query: $query) {\n    id\n    lot\n    name\n    isDisabled\n  }\n}\n\nquery UserMetadataRecommendations {\n  userMetadataRecommendations\n}\n\nquery UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {\n  userUpcomingCalendarEvents(input: $input) {\n    ...CalendarEventPart\n  }\n}\n\nquery UserCalendarEvents($input: UserCalendarEventInput!) {\n  userCalendarEvents(input: $input) {\n    date\n    events {\n      ...CalendarEventPart\n    }\n  }\n}\n\nquery MetadataPartialDetails($metadataId: String!) {\n  metadataPartialDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    image\n    publishYear\n  }\n}\n\nquery MetadataGroupsList($input: MetadataGroupsListInput!) {\n  metadataGroupsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery PeopleList($input: PeopleListInput!) {\n  peopleList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAccessLinks {\n  userAccessLinks {\n    id\n    name\n    isDemo\n    createdOn\n    expiresOn\n    timesUsed\n    isRevoked\n    maximumUses\n    isAccountDefault\n    isMutationAllowed\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    name\n    level\n    force\n    source\n    muscles\n    mechanic\n    equipment\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery GenreDetails($input: GenreDetailsInput!) {\n  genreDetails(input: $input) {\n    details {\n      id\n      name\n      numItems\n    }\n    contents {\n      details {\n        total\n        nextPage\n      }\n      items\n    }\n  }\n}\n\nquery CollectionContents($input: CollectionContentsInput!) {\n  collectionContents(input: $input) {\n    user {\n      id\n      name\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    results {\n      details {\n        total\n        nextPage\n      }\n      items {\n        entityId\n        entityLot\n      }\n    }\n    details {\n      name\n      description\n      createdOn\n    }\n  }\n}\n\nquery CoreDetails {\n  coreDetails {\n    version\n    docsLink\n    pageSize\n    websiteUrl\n    smtpEnabled\n    oidcEnabled\n    signupAllowed\n    repositoryLink\n    disableTelemetry\n    tokenValidForDays\n    localAuthDisabled\n    fileStorageEnabled\n    peopleSearchSources\n    isServerKeyValidated\n    metadataGroupSourceLotMappings {\n      lot\n      source\n    }\n    metadataLotSourceMappings {\n      lot\n      sources\n    }\n    metadataProviderLanguages {\n      source\n      default\n      supported\n    }\n    frontend {\n      url\n      oidcButtonLabel\n      dashboardMessage\n      umami {\n        domains\n        scriptUrl\n        websiteId\n      }\n    }\n    exerciseParameters {\n      downloadRequired\n      filters {\n        type\n        level\n        force\n        mechanic\n        equipment\n        muscle\n      }\n      lotMapping {\n        lot\n        bests\n      }\n    }\n  }\n}\n\nquery MetadataGroupDetails($metadataGroupId: String!) {\n  metadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    contents\n    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}": types.GetOidcRedirectUrlDocument,
     "fragment SeenPodcastExtraInformationPart on SeenPodcastExtraInformation {\n  episode\n}\n\nfragment SeenShowExtraInformationPart on SeenShowExtraInformation {\n  episode\n  season\n}\n\nfragment SeenAnimeExtraInformationPart on SeenAnimeExtraInformation {\n  episode\n}\n\nfragment SeenMangaExtraInformationPart on SeenMangaExtraInformation {\n  volume\n  chapter\n}\n\nfragment CalendarEventPart on GraphqlCalendarEvent {\n  date\n  metadataId\n  metadataLot\n  episodeName\n  metadataTitle\n  metadataImage\n  calendarEventId\n  showExtraInformation {\n    ...SeenShowExtraInformationPart\n  }\n  podcastExtraInformation {\n    ...SeenPodcastExtraInformationPart\n  }\n  animeExtraInformation {\n    ...SeenAnimeExtraInformationPart\n  }\n}\n\nfragment SeenPart on Seen {\n  id\n  state\n  progress\n  reviewId\n  startedOn\n  finishedOn\n  lastUpdatedOn\n  manualTimeSpent\n  numTimesUpdated\n  providerWatchedOn\n  showExtraInformation {\n    ...SeenShowExtraInformationPart\n  }\n  podcastExtraInformation {\n    ...SeenPodcastExtraInformationPart\n  }\n  animeExtraInformation {\n    ...SeenAnimeExtraInformationPart\n  }\n  mangaExtraInformation {\n    ...SeenMangaExtraInformationPart\n  }\n}\n\nfragment MetadataSearchItemPart on MetadataSearchItem {\n  title\n  image\n  identifier\n  publishYear\n}\n\nfragment WorkoutOrExerciseTotalsPart on WorkoutOrExerciseTotals {\n  reps\n  weight\n  distance\n  duration\n  restTime\n  personalBestsAchieved\n}\n\nfragment EntityAssetsPart on EntityAssets {\n  images\n  videos\n}\n\nfragment WorkoutSetStatisticPart on WorkoutSetStatistic {\n  reps\n  pace\n  oneRm\n  weight\n  volume\n  duration\n  distance\n}\n\nfragment WorkoutSetRecordPart on WorkoutSetRecord {\n  lot\n  personalBests\n  statistic {\n    ...WorkoutSetStatisticPart\n  }\n}\n\nfragment WorkoutSummaryPart on WorkoutSummary {\n  total {\n    ...WorkoutOrExerciseTotalsPart\n  }\n  exercises {\n    id\n    lot\n    numSets\n    bestSet {\n      ...WorkoutSetRecordPart\n    }\n  }\n  focused {\n    lots {\n      lot\n      exercises\n    }\n    levels {\n      level\n      exercises\n    }\n    forces {\n      force\n      exercises\n    }\n    muscles {\n      muscle\n      exercises\n    }\n    equipments {\n      equipment\n      exercises\n    }\n  }\n}\n\nfragment CollectionPart on Collection {\n  id\n  name\n  userId\n}\n\nfragment ReviewItemPart on ReviewItem {\n  id\n  rating\n  postedOn\n  isSpoiler\n  visibility\n  textOriginal\n  textRendered\n  seenItemsAssociatedWith\n  postedBy {\n    id\n    name\n  }\n  comments {\n    id\n    text\n    likedBy\n    createdOn\n    user {\n      id\n      name\n    }\n  }\n  showExtraInformation {\n    season\n    episode\n  }\n  podcastExtraInformation {\n    episode\n  }\n  animeExtraInformation {\n    ...SeenAnimeExtraInformationPart\n  }\n  mangaExtraInformation {\n    ...SeenMangaExtraInformationPart\n  }\n}\n\nfragment WorkoutInformationPart on WorkoutInformation {\n  comment\n  assets {\n    ...EntityAssetsPart\n  }\n  supersets {\n    color\n    exercises\n  }\n  exercises {\n    id\n    lot\n    notes\n    total {\n      ...WorkoutOrExerciseTotalsPart\n    }\n    assets {\n      ...EntityAssetsPart\n    }\n    sets {\n      lot\n      rpe\n      note\n      restTime\n      confirmedAt\n      personalBests\n      statistic {\n        ...WorkoutSetStatisticPart\n      }\n    }\n  }\n}\n\nfragment SetRestTimersPart on SetRestTimersSettings {\n  drop\n  warmup\n  normal\n  failure\n}\n\nfragment PersonDetailsGroupedByRolePart on PersonDetailsGroupedByRole {\n  name\n  count\n  items {\n    entityId\n    character\n  }\n}": types.SeenPodcastExtraInformationPartFragmentDoc,
 };
 
@@ -47,11 +47,11 @@ export function graphql(source: "mutation RegisterUser($input: RegisterUserInput
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
-export function graphql(source: "query MetadataDetails($metadataId: String!) {\n  metadataDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    source\n    isNsfw\n    isPartial\n    sourceUrl\n    identifier\n    description\n    suggestions\n    publishYear\n    publishDate\n    providerRating\n    createdByUserId\n    productionStatus\n    originalLanguage\n    genres {\n      id\n      name\n    }\n    group {\n      id\n      name\n      part\n    }\n    assets {\n      images\n      videos {\n        videoId\n        source\n      }\n    }\n    creators {\n      name\n      items {\n        id\n        name\n        image\n        character\n      }\n    }\n    watchProviders {\n      name\n      image\n      languages\n    }\n    animeSpecifics {\n      episodes\n    }\n    audioBookSpecifics {\n      runtime\n    }\n    bookSpecifics {\n      pages\n    }\n    movieSpecifics {\n      runtime\n    }\n    mangaSpecifics {\n      volumes\n      chapters\n    }\n    podcastSpecifics {\n      episodes {\n        id\n        title\n        overview\n        thumbnail\n        number\n        runtime\n        publishDate\n      }\n      totalEpisodes\n    }\n    showSpecifics {\n      totalSeasons\n      totalEpisodes\n      runtime\n      seasons {\n        id\n        seasonNumber\n        name\n        overview\n        backdropImages\n        posterImages\n        episodes {\n          id\n          name\n          posterImages\n          episodeNumber\n          publishDate\n          name\n          overview\n          runtime\n        }\n      }\n    }\n    visualNovelSpecifics {\n      length\n    }\n    videoGameSpecifics {\n      platforms\n    }\n    musicSpecifics {\n      duration\n      viewCount\n      byVariousArtists\n    }\n  }\n}"): (typeof documents)["query MetadataDetails($metadataId: String!) {\n  metadataDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    source\n    isNsfw\n    isPartial\n    sourceUrl\n    identifier\n    description\n    suggestions\n    publishYear\n    publishDate\n    providerRating\n    createdByUserId\n    productionStatus\n    originalLanguage\n    genres {\n      id\n      name\n    }\n    group {\n      id\n      name\n      part\n    }\n    assets {\n      images\n      videos {\n        videoId\n        source\n      }\n    }\n    creators {\n      name\n      items {\n        id\n        name\n        image\n        character\n      }\n    }\n    watchProviders {\n      name\n      image\n      languages\n    }\n    animeSpecifics {\n      episodes\n    }\n    audioBookSpecifics {\n      runtime\n    }\n    bookSpecifics {\n      pages\n    }\n    movieSpecifics {\n      runtime\n    }\n    mangaSpecifics {\n      volumes\n      chapters\n    }\n    podcastSpecifics {\n      episodes {\n        id\n        title\n        overview\n        thumbnail\n        number\n        runtime\n        publishDate\n      }\n      totalEpisodes\n    }\n    showSpecifics {\n      totalSeasons\n      totalEpisodes\n      runtime\n      seasons {\n        id\n        seasonNumber\n        name\n        overview\n        backdropImages\n        posterImages\n        episodes {\n          id\n          name\n          posterImages\n          episodeNumber\n          publishDate\n          name\n          overview\n          runtime\n        }\n      }\n    }\n    visualNovelSpecifics {\n      length\n    }\n    videoGameSpecifics {\n      platforms\n    }\n    musicSpecifics {\n      duration\n      viewCount\n      byVariousArtists\n    }\n  }\n}"];
+export function graphql(source: "query MetadataDetails($metadataId: String!) {\n  metadataDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    source\n    isNsfw\n    isPartial\n    sourceUrl\n    identifier\n    description\n    suggestions\n    publishYear\n    publishDate\n    providerRating\n    createdByUserId\n    productionStatus\n    originalLanguage\n    animeSpecifics {\n      episodes\n    }\n    audioBookSpecifics {\n      runtime\n    }\n    movieSpecifics {\n      runtime\n    }\n    genres {\n      id\n      name\n    }\n    group {\n      id\n      name\n      part\n    }\n    watchProviders {\n      name\n      image\n      languages\n    }\n    bookSpecifics {\n      pages\n      isCompilation\n    }\n    mangaSpecifics {\n      volumes\n      chapters\n    }\n    assets {\n      images\n      videos {\n        videoId\n        source\n      }\n    }\n    creators {\n      name\n      items {\n        id\n        name\n        image\n        character\n      }\n    }\n    podcastSpecifics {\n      episodes {\n        id\n        title\n        overview\n        thumbnail\n        number\n        runtime\n        publishDate\n      }\n      totalEpisodes\n    }\n    showSpecifics {\n      totalSeasons\n      totalEpisodes\n      runtime\n      seasons {\n        id\n        seasonNumber\n        name\n        overview\n        backdropImages\n        posterImages\n        episodes {\n          id\n          name\n          posterImages\n          episodeNumber\n          publishDate\n          name\n          overview\n          runtime\n        }\n      }\n    }\n    visualNovelSpecifics {\n      length\n    }\n    videoGameSpecifics {\n      platforms\n    }\n    musicSpecifics {\n      duration\n      viewCount\n      byVariousArtists\n    }\n  }\n}"): (typeof documents)["query MetadataDetails($metadataId: String!) {\n  metadataDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    source\n    isNsfw\n    isPartial\n    sourceUrl\n    identifier\n    description\n    suggestions\n    publishYear\n    publishDate\n    providerRating\n    createdByUserId\n    productionStatus\n    originalLanguage\n    animeSpecifics {\n      episodes\n    }\n    audioBookSpecifics {\n      runtime\n    }\n    movieSpecifics {\n      runtime\n    }\n    genres {\n      id\n      name\n    }\n    group {\n      id\n      name\n      part\n    }\n    watchProviders {\n      name\n      image\n      languages\n    }\n    bookSpecifics {\n      pages\n      isCompilation\n    }\n    mangaSpecifics {\n      volumes\n      chapters\n    }\n    assets {\n      images\n      videos {\n        videoId\n        source\n      }\n    }\n    creators {\n      name\n      items {\n        id\n        name\n        image\n        character\n      }\n    }\n    podcastSpecifics {\n      episodes {\n        id\n        title\n        overview\n        thumbnail\n        number\n        runtime\n        publishDate\n      }\n      totalEpisodes\n    }\n    showSpecifics {\n      totalSeasons\n      totalEpisodes\n      runtime\n      seasons {\n        id\n        seasonNumber\n        name\n        overview\n        backdropImages\n        posterImages\n        episodes {\n          id\n          name\n          posterImages\n          episodeNumber\n          publishDate\n          name\n          overview\n          runtime\n        }\n      }\n    }\n    visualNovelSpecifics {\n      length\n    }\n    videoGameSpecifics {\n      platforms\n    }\n    musicSpecifics {\n      duration\n      viewCount\n      byVariousArtists\n    }\n  }\n}"];
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
-export function graphql(source: "query PersonDetails($personId: String!) {\n  personDetails(personId: $personId) {\n    associatedMetadata {\n      ...PersonDetailsGroupedByRolePart\n    }\n    associatedMetadataGroups {\n      ...PersonDetailsGroupedByRolePart\n    }\n    details {\n      id\n      name\n      place\n      source\n      gender\n      website\n      deathDate\n      birthDate\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}"): (typeof documents)["query PersonDetails($personId: String!) {\n  personDetails(personId: $personId) {\n    associatedMetadata {\n      ...PersonDetailsGroupedByRolePart\n    }\n    associatedMetadataGroups {\n      ...PersonDetailsGroupedByRolePart\n    }\n    details {\n      id\n      name\n      place\n      source\n      gender\n      website\n      deathDate\n      birthDate\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}"];
+export function graphql(source: "query PersonDetails($personId: String!) {\n  personDetails(personId: $personId) {\n    associatedMetadata {\n      ...PersonDetailsGroupedByRolePart\n    }\n    associatedMetadataGroups {\n      ...PersonDetailsGroupedByRolePart\n    }\n    details {\n      id\n      name\n      place\n      source\n      gender\n      website\n      deathDate\n      birthDate\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n      alternateNames\n    }\n  }\n}"): (typeof documents)["query PersonDetails($personId: String!) {\n  personDetails(personId: $personId) {\n    associatedMetadata {\n      ...PersonDetailsGroupedByRolePart\n    }\n    associatedMetadataGroups {\n      ...PersonDetailsGroupedByRolePart\n    }\n    details {\n      id\n      name\n      place\n      source\n      gender\n      website\n      deathDate\n      birthDate\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n      alternateNames\n    }\n  }\n}"];
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
@@ -75,7 +75,7 @@ export function graphql(source: "query UserMetadataDetails($metadataId: String!)
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
-export function graphql(source: "query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    key\n    size\n    endedAt\n    startedAt\n  }\n}\n\nquery UserCollectionsList($name: String) {\n  userCollectionsList(name: $name) {\n    id\n    name\n    count\n    isDefault\n    description\n    creator {\n      id\n      name\n    }\n    collaborators {\n      id\n      name\n    }\n    informationTemplate {\n      lot\n      name\n      required\n      description\n      defaultValue\n    }\n  }\n}\n\nquery UserIntegrations {\n  userIntegrations {\n    id\n    lot\n    provider\n    createdOn\n    isDisabled\n    maximumProgress\n    minimumProgress\n    lastTriggeredOn\n    syncToOwnedCollection\n  }\n}\n\nquery UserNotificationPlatforms {\n  userNotificationPlatforms {\n    id\n    lot\n    createdOn\n    isDisabled\n    description\n  }\n}\n\nquery UsersList($query: String) {\n  usersList(query: $query) {\n    id\n    lot\n    name\n    isDisabled\n  }\n}\n\nquery UserMetadataRecommendations {\n  userMetadataRecommendations\n}\n\nquery UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {\n  userUpcomingCalendarEvents(input: $input) {\n    ...CalendarEventPart\n  }\n}\n\nquery UserCalendarEvents($input: UserCalendarEventInput!) {\n  userCalendarEvents(input: $input) {\n    date\n    events {\n      ...CalendarEventPart\n    }\n  }\n}\n\nquery MetadataPartialDetails($metadataId: String!) {\n  metadataPartialDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    image\n    publishYear\n  }\n}\n\nquery MetadataGroupsList($input: MetadataGroupsListInput!) {\n  metadataGroupsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery PeopleList($input: PeopleListInput!) {\n  peopleList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAccessLinks {\n  userAccessLinks {\n    id\n    name\n    isDemo\n    createdOn\n    expiresOn\n    timesUsed\n    isRevoked\n    maximumUses\n    isAccountDefault\n    isMutationAllowed\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    name\n    level\n    force\n    source\n    muscles\n    mechanic\n    equipment\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery GenreDetails($input: GenreDetailsInput!) {\n  genreDetails(input: $input) {\n    details {\n      id\n      name\n      numItems\n    }\n    contents {\n      details {\n        total\n        nextPage\n      }\n      items\n    }\n  }\n}\n\nquery CollectionContents($input: CollectionContentsInput!) {\n  collectionContents(input: $input) {\n    user {\n      id\n      name\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    results {\n      details {\n        total\n        nextPage\n      }\n      items {\n        entityId\n        entityLot\n      }\n    }\n    details {\n      name\n      description\n      createdOn\n    }\n  }\n}\n\nquery CoreDetails {\n  coreDetails {\n    version\n    docsLink\n    pageSize\n    websiteUrl\n    smtpEnabled\n    oidcEnabled\n    signupAllowed\n    repositoryLink\n    disableTelemetry\n    tokenValidForDays\n    localAuthDisabled\n    fileStorageEnabled\n    isServerKeyValidated\n    metadataLotSourceMappings {\n      lot\n      sources\n    }\n    metadataProviderLanguages {\n      source\n      default\n      supported\n    }\n    frontend {\n      url\n      oidcButtonLabel\n      dashboardMessage\n      umami {\n        domains\n        scriptUrl\n        websiteId\n      }\n    }\n    exerciseParameters {\n      downloadRequired\n      filters {\n        type\n        level\n        force\n        mechanic\n        equipment\n        muscle\n      }\n      lotMapping {\n        lot\n        bests\n      }\n    }\n  }\n}\n\nquery MetadataGroupDetails($metadataGroupId: String!) {\n  metadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    contents\n    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}"): (typeof documents)["query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    key\n    size\n    endedAt\n    startedAt\n  }\n}\n\nquery UserCollectionsList($name: String) {\n  userCollectionsList(name: $name) {\n    id\n    name\n    count\n    isDefault\n    description\n    creator {\n      id\n      name\n    }\n    collaborators {\n      id\n      name\n    }\n    informationTemplate {\n      lot\n      name\n      required\n      description\n      defaultValue\n    }\n  }\n}\n\nquery UserIntegrations {\n  userIntegrations {\n    id\n    lot\n    provider\n    createdOn\n    isDisabled\n    maximumProgress\n    minimumProgress\n    lastTriggeredOn\n    syncToOwnedCollection\n  }\n}\n\nquery UserNotificationPlatforms {\n  userNotificationPlatforms {\n    id\n    lot\n    createdOn\n    isDisabled\n    description\n  }\n}\n\nquery UsersList($query: String) {\n  usersList(query: $query) {\n    id\n    lot\n    name\n    isDisabled\n  }\n}\n\nquery UserMetadataRecommendations {\n  userMetadataRecommendations\n}\n\nquery UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {\n  userUpcomingCalendarEvents(input: $input) {\n    ...CalendarEventPart\n  }\n}\n\nquery UserCalendarEvents($input: UserCalendarEventInput!) {\n  userCalendarEvents(input: $input) {\n    date\n    events {\n      ...CalendarEventPart\n    }\n  }\n}\n\nquery MetadataPartialDetails($metadataId: String!) {\n  metadataPartialDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    image\n    publishYear\n  }\n}\n\nquery MetadataGroupsList($input: MetadataGroupsListInput!) {\n  metadataGroupsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery PeopleList($input: PeopleListInput!) {\n  peopleList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAccessLinks {\n  userAccessLinks {\n    id\n    name\n    isDemo\n    createdOn\n    expiresOn\n    timesUsed\n    isRevoked\n    maximumUses\n    isAccountDefault\n    isMutationAllowed\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    name\n    level\n    force\n    source\n    muscles\n    mechanic\n    equipment\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery GenreDetails($input: GenreDetailsInput!) {\n  genreDetails(input: $input) {\n    details {\n      id\n      name\n      numItems\n    }\n    contents {\n      details {\n        total\n        nextPage\n      }\n      items\n    }\n  }\n}\n\nquery CollectionContents($input: CollectionContentsInput!) {\n  collectionContents(input: $input) {\n    user {\n      id\n      name\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    results {\n      details {\n        total\n        nextPage\n      }\n      items {\n        entityId\n        entityLot\n      }\n    }\n    details {\n      name\n      description\n      createdOn\n    }\n  }\n}\n\nquery CoreDetails {\n  coreDetails {\n    version\n    docsLink\n    pageSize\n    websiteUrl\n    smtpEnabled\n    oidcEnabled\n    signupAllowed\n    repositoryLink\n    disableTelemetry\n    tokenValidForDays\n    localAuthDisabled\n    fileStorageEnabled\n    isServerKeyValidated\n    metadataLotSourceMappings {\n      lot\n      sources\n    }\n    metadataProviderLanguages {\n      source\n      default\n      supported\n    }\n    frontend {\n      url\n      oidcButtonLabel\n      dashboardMessage\n      umami {\n        domains\n        scriptUrl\n        websiteId\n      }\n    }\n    exerciseParameters {\n      downloadRequired\n      filters {\n        type\n        level\n        force\n        mechanic\n        equipment\n        muscle\n      }\n      lotMapping {\n        lot\n        bests\n      }\n    }\n  }\n}\n\nquery MetadataGroupDetails($metadataGroupId: String!) {\n  metadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    contents\n    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}"];
+export function graphql(source: "query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    key\n    size\n    endedAt\n    startedAt\n  }\n}\n\nquery UserCollectionsList($name: String) {\n  userCollectionsList(name: $name) {\n    id\n    name\n    count\n    isDefault\n    description\n    creator {\n      id\n      name\n    }\n    collaborators {\n      id\n      name\n    }\n    informationTemplate {\n      lot\n      name\n      required\n      description\n      defaultValue\n    }\n  }\n}\n\nquery UserIntegrations {\n  userIntegrations {\n    id\n    lot\n    provider\n    createdOn\n    isDisabled\n    maximumProgress\n    minimumProgress\n    lastTriggeredOn\n    syncToOwnedCollection\n  }\n}\n\nquery UserNotificationPlatforms {\n  userNotificationPlatforms {\n    id\n    lot\n    createdOn\n    isDisabled\n    description\n  }\n}\n\nquery UsersList($query: String) {\n  usersList(query: $query) {\n    id\n    lot\n    name\n    isDisabled\n  }\n}\n\nquery UserMetadataRecommendations {\n  userMetadataRecommendations\n}\n\nquery UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {\n  userUpcomingCalendarEvents(input: $input) {\n    ...CalendarEventPart\n  }\n}\n\nquery UserCalendarEvents($input: UserCalendarEventInput!) {\n  userCalendarEvents(input: $input) {\n    date\n    events {\n      ...CalendarEventPart\n    }\n  }\n}\n\nquery MetadataPartialDetails($metadataId: String!) {\n  metadataPartialDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    image\n    publishYear\n  }\n}\n\nquery MetadataGroupsList($input: MetadataGroupsListInput!) {\n  metadataGroupsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery PeopleList($input: PeopleListInput!) {\n  peopleList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAccessLinks {\n  userAccessLinks {\n    id\n    name\n    isDemo\n    createdOn\n    expiresOn\n    timesUsed\n    isRevoked\n    maximumUses\n    isAccountDefault\n    isMutationAllowed\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    name\n    level\n    force\n    source\n    muscles\n    mechanic\n    equipment\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery GenreDetails($input: GenreDetailsInput!) {\n  genreDetails(input: $input) {\n    details {\n      id\n      name\n      numItems\n    }\n    contents {\n      details {\n        total\n        nextPage\n      }\n      items\n    }\n  }\n}\n\nquery CollectionContents($input: CollectionContentsInput!) {\n  collectionContents(input: $input) {\n    user {\n      id\n      name\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    results {\n      details {\n        total\n        nextPage\n      }\n      items {\n        entityId\n        entityLot\n      }\n    }\n    details {\n      name\n      description\n      createdOn\n    }\n  }\n}\n\nquery CoreDetails {\n  coreDetails {\n    version\n    docsLink\n    pageSize\n    websiteUrl\n    smtpEnabled\n    oidcEnabled\n    signupAllowed\n    repositoryLink\n    disableTelemetry\n    tokenValidForDays\n    localAuthDisabled\n    fileStorageEnabled\n    peopleSearchSources\n    isServerKeyValidated\n    metadataGroupSourceLotMappings {\n      lot\n      source\n    }\n    metadataLotSourceMappings {\n      lot\n      sources\n    }\n    metadataProviderLanguages {\n      source\n      default\n      supported\n    }\n    frontend {\n      url\n      oidcButtonLabel\n      dashboardMessage\n      umami {\n        domains\n        scriptUrl\n        websiteId\n      }\n    }\n    exerciseParameters {\n      downloadRequired\n      filters {\n        type\n        level\n        force\n        mechanic\n        equipment\n        muscle\n      }\n      lotMapping {\n        lot\n        bests\n      }\n    }\n  }\n}\n\nquery MetadataGroupDetails($metadataGroupId: String!) {\n  metadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    contents\n    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}"): (typeof documents)["query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    key\n    size\n    endedAt\n    startedAt\n  }\n}\n\nquery UserCollectionsList($name: String) {\n  userCollectionsList(name: $name) {\n    id\n    name\n    count\n    isDefault\n    description\n    creator {\n      id\n      name\n    }\n    collaborators {\n      id\n      name\n    }\n    informationTemplate {\n      lot\n      name\n      required\n      description\n      defaultValue\n    }\n  }\n}\n\nquery UserIntegrations {\n  userIntegrations {\n    id\n    lot\n    provider\n    createdOn\n    isDisabled\n    maximumProgress\n    minimumProgress\n    lastTriggeredOn\n    syncToOwnedCollection\n  }\n}\n\nquery UserNotificationPlatforms {\n  userNotificationPlatforms {\n    id\n    lot\n    createdOn\n    isDisabled\n    description\n  }\n}\n\nquery UsersList($query: String) {\n  usersList(query: $query) {\n    id\n    lot\n    name\n    isDisabled\n  }\n}\n\nquery UserMetadataRecommendations {\n  userMetadataRecommendations\n}\n\nquery UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {\n  userUpcomingCalendarEvents(input: $input) {\n    ...CalendarEventPart\n  }\n}\n\nquery UserCalendarEvents($input: UserCalendarEventInput!) {\n  userCalendarEvents(input: $input) {\n    date\n    events {\n      ...CalendarEventPart\n    }\n  }\n}\n\nquery MetadataPartialDetails($metadataId: String!) {\n  metadataPartialDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    image\n    publishYear\n  }\n}\n\nquery MetadataGroupsList($input: MetadataGroupsListInput!) {\n  metadataGroupsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery PeopleList($input: PeopleListInput!) {\n  peopleList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAccessLinks {\n  userAccessLinks {\n    id\n    name\n    isDemo\n    createdOn\n    expiresOn\n    timesUsed\n    isRevoked\n    maximumUses\n    isAccountDefault\n    isMutationAllowed\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    name\n    level\n    force\n    source\n    muscles\n    mechanic\n    equipment\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery GenreDetails($input: GenreDetailsInput!) {\n  genreDetails(input: $input) {\n    details {\n      id\n      name\n      numItems\n    }\n    contents {\n      details {\n        total\n        nextPage\n      }\n      items\n    }\n  }\n}\n\nquery CollectionContents($input: CollectionContentsInput!) {\n  collectionContents(input: $input) {\n    user {\n      id\n      name\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    results {\n      details {\n        total\n        nextPage\n      }\n      items {\n        entityId\n        entityLot\n      }\n    }\n    details {\n      name\n      description\n      createdOn\n    }\n  }\n}\n\nquery CoreDetails {\n  coreDetails {\n    version\n    docsLink\n    pageSize\n    websiteUrl\n    smtpEnabled\n    oidcEnabled\n    signupAllowed\n    repositoryLink\n    disableTelemetry\n    tokenValidForDays\n    localAuthDisabled\n    fileStorageEnabled\n    peopleSearchSources\n    isServerKeyValidated\n    metadataGroupSourceLotMappings {\n      lot\n      source\n    }\n    metadataLotSourceMappings {\n      lot\n      sources\n    }\n    metadataProviderLanguages {\n      source\n      default\n      supported\n    }\n    frontend {\n      url\n      oidcButtonLabel\n      dashboardMessage\n      umami {\n        domains\n        scriptUrl\n        websiteId\n      }\n    }\n    exerciseParameters {\n      downloadRequired\n      filters {\n        type\n        level\n        force\n        mechanic\n        equipment\n        muscle\n      }\n      lotMapping {\n        lot\n        bests\n      }\n    }\n  }\n}\n\nquery MetadataGroupDetails($metadataGroupId: String!) {\n  metadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    contents\n    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}"];
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
diff --git a/libs/generated/src/graphql/backend/graphql.ts b/libs/generated/src/graphql/backend/graphql.ts
index adf7a38722..6afacaeca4 100644
--- a/libs/generated/src/graphql/backend/graphql.ts
+++ b/libs/generated/src/graphql/backend/graphql.ts
@@ -123,10 +123,12 @@ export enum BackgroundJob {
 }
 
 export type BookSpecifics = {
+  isCompilation?: Maybe<Scalars['Boolean']['output']>;
   pages?: Maybe<Scalars['Int']['output']>;
 };
 
 export type BookSpecificsInput = {
+  isCompilation?: InputMaybe<Scalars['Boolean']['input']>;
   pages?: InputMaybe<Scalars['Int']['input']>;
 };
 
@@ -234,10 +236,12 @@ export type CoreDetails = {
   frontend: FrontendConfig;
   isServerKeyValidated: Scalars['Boolean']['output'];
   localAuthDisabled: Scalars['Boolean']['output'];
+  metadataGroupSourceLotMappings: Array<MetadataGroupSourceLotMapping>;
   metadataLotSourceMappings: Array<MetadataLotSourceMappings>;
   metadataProviderLanguages: Array<ProviderLanguageInformation>;
   oidcEnabled: Scalars['Boolean']['output'];
   pageSize: Scalars['Int']['output'];
+  peopleSearchSources: Array<MediaSource>;
   repositoryLink: Scalars['String']['output'];
   signupAllowed: Scalars['Boolean']['output'];
   smtpEnabled: Scalars['Boolean']['output'];
@@ -1014,6 +1018,7 @@ export enum MediaSource {
   Audible = 'AUDIBLE',
   Custom = 'CUSTOM',
   GoogleBooks = 'GOOGLE_BOOKS',
+  Hardcover = 'HARDCOVER',
   Igdb = 'IGDB',
   Itunes = 'ITUNES',
   Listennotes = 'LISTENNOTES',
@@ -1073,6 +1078,11 @@ export type MetadataGroupSearchResults = {
   items: Array<MetadataGroupSearchItem>;
 };
 
+export type MetadataGroupSourceLotMapping = {
+  lot: MediaLot;
+  source: MediaSource;
+};
+
 export type MetadataGroupsListInput = {
   filter?: InputMaybe<MediaFilter>;
   invertCollection?: InputMaybe<Scalars['Boolean']['input']>;
@@ -1586,6 +1596,7 @@ export type PeopleSearchResults = {
 };
 
 export type Person = {
+  alternateNames?: Maybe<Array<Scalars['String']['output']>>;
   birthDate?: Maybe<Scalars['NaiveDate']['output']>;
   createdOn: Scalars['DateTime']['output'];
   deathDate?: Maybe<Scalars['NaiveDate']['output']>;
@@ -1629,6 +1640,7 @@ export type PersonSortInput = {
 
 export type PersonSourceSpecificsInput = {
   isAnilistStudio?: InputMaybe<Scalars['Boolean']['input']>;
+  isHardcoverPublisher?: InputMaybe<Scalars['Boolean']['input']>;
   isTmdbCompany?: InputMaybe<Scalars['Boolean']['input']>;
 };
 
@@ -3369,14 +3381,14 @@ export type MetadataDetailsQueryVariables = Exact<{
 }>;
 
 
-export type MetadataDetailsQuery = { metadataDetails: { id: string, lot: MediaLot, title: string, source: MediaSource, isNsfw?: boolean | null, isPartial?: boolean | null, sourceUrl?: string | null, identifier: string, description?: string | null, suggestions: Array<string>, publishYear?: number | null, publishDate?: string | null, providerRating?: string | null, createdByUserId?: string | null, productionStatus?: string | null, originalLanguage?: string | null, genres: Array<{ id: string, name: string }>, group: Array<{ id: string, name: string, part: number }>, assets: { images: Array<string>, videos: Array<{ videoId: string, source: MetadataVideoSource }> }, creators: Array<{ name: string, items: Array<{ id?: string | null, name: string, image?: string | null, character?: string | null }> }>, watchProviders: Array<{ name: string, image?: string | null, languages: Array<string> }>, animeSpecifics?: { episodes?: number | null } | null, audioBookSpecifics?: { runtime?: number | null } | null, bookSpecifics?: { pages?: number | null } | null, movieSpecifics?: { runtime?: number | null } | null, mangaSpecifics?: { volumes?: number | null, chapters?: string | null } | null, podcastSpecifics?: { totalEpisodes: number, episodes: Array<{ id: string, title: string, overview?: string | null, thumbnail?: string | null, number: number, runtime?: number | null, publishDate: string }> } | null, showSpecifics?: { totalSeasons?: number | null, totalEpisodes?: number | null, runtime?: number | null, seasons: Array<{ id: number, seasonNumber: number, name: string, overview?: string | null, backdropImages: Array<string>, posterImages: Array<string>, episodes: Array<{ id: number, name: string, posterImages: Array<string>, episodeNumber: number, publishDate?: string | null, overview?: string | null, runtime?: number | null }> }> } | null, visualNovelSpecifics?: { length?: number | null } | null, videoGameSpecifics?: { platforms: Array<string> } | null, musicSpecifics?: { duration?: number | null, viewCount?: number | null, byVariousArtists?: boolean | null } | null } };
+export type MetadataDetailsQuery = { metadataDetails: { id: string, lot: MediaLot, title: string, source: MediaSource, isNsfw?: boolean | null, isPartial?: boolean | null, sourceUrl?: string | null, identifier: string, description?: string | null, suggestions: Array<string>, publishYear?: number | null, publishDate?: string | null, providerRating?: string | null, createdByUserId?: string | null, productionStatus?: string | null, originalLanguage?: string | null, animeSpecifics?: { episodes?: number | null } | null, audioBookSpecifics?: { runtime?: number | null } | null, movieSpecifics?: { runtime?: number | null } | null, genres: Array<{ id: string, name: string }>, group: Array<{ id: string, name: string, part: number }>, watchProviders: Array<{ name: string, image?: string | null, languages: Array<string> }>, bookSpecifics?: { pages?: number | null, isCompilation?: boolean | null } | null, mangaSpecifics?: { volumes?: number | null, chapters?: string | null } | null, assets: { images: Array<string>, videos: Array<{ videoId: string, source: MetadataVideoSource }> }, creators: Array<{ name: string, items: Array<{ id?: string | null, name: string, image?: string | null, character?: string | null }> }>, podcastSpecifics?: { totalEpisodes: number, episodes: Array<{ id: string, title: string, overview?: string | null, thumbnail?: string | null, number: number, runtime?: number | null, publishDate: string }> } | null, showSpecifics?: { totalSeasons?: number | null, totalEpisodes?: number | null, runtime?: number | null, seasons: Array<{ id: number, seasonNumber: number, name: string, overview?: string | null, backdropImages: Array<string>, posterImages: Array<string>, episodes: Array<{ id: number, name: string, posterImages: Array<string>, episodeNumber: number, publishDate?: string | null, overview?: string | null, runtime?: number | null }> }> } | null, visualNovelSpecifics?: { length?: number | null } | null, videoGameSpecifics?: { platforms: Array<string> } | null, musicSpecifics?: { duration?: number | null, viewCount?: number | null, byVariousArtists?: boolean | null } | null } };
 
 export type PersonDetailsQueryVariables = Exact<{
   personId: Scalars['String']['input'];
 }>;
 
 
-export type PersonDetailsQuery = { personDetails: { associatedMetadata: Array<{ name: string, count: number, items: Array<{ entityId: string, character?: string | null }> }>, associatedMetadataGroups: Array<{ name: string, count: number, items: Array<{ entityId: string, character?: string | null }> }>, details: { id: string, name: string, place?: string | null, source: MediaSource, gender?: string | null, website?: string | null, deathDate?: string | null, birthDate?: string | null, isPartial?: boolean | null, sourceUrl?: string | null, identifier: string, description?: string | null, displayImages: Array<string> } } };
+export type PersonDetailsQuery = { personDetails: { associatedMetadata: Array<{ name: string, count: number, items: Array<{ entityId: string, character?: string | null }> }>, associatedMetadataGroups: Array<{ name: string, count: number, items: Array<{ entityId: string, character?: string | null }> }>, details: { id: string, name: string, place?: string | null, source: MediaSource, gender?: string | null, website?: string | null, deathDate?: string | null, birthDate?: string | null, isPartial?: boolean | null, sourceUrl?: string | null, identifier: string, description?: string | null, displayImages: Array<string>, alternateNames?: Array<string> | null } } };
 
 export type UserAnalyticsQueryVariables = Exact<{
   input: UserAnalyticsInput;
@@ -3554,7 +3566,7 @@ export type CollectionContentsQuery = { collectionContents: { user: { id: string
 export type CoreDetailsQueryVariables = Exact<{ [key: string]: never; }>;
 
 
-export type CoreDetailsQuery = { coreDetails: { version: string, docsLink: string, pageSize: number, websiteUrl: string, smtpEnabled: boolean, oidcEnabled: boolean, signupAllowed: boolean, repositoryLink: string, disableTelemetry: boolean, tokenValidForDays: number, localAuthDisabled: boolean, fileStorageEnabled: boolean, isServerKeyValidated: boolean, metadataLotSourceMappings: Array<{ lot: MediaLot, sources: Array<MediaSource> }>, metadataProviderLanguages: Array<{ source: MediaSource, default: string, supported: Array<string> }>, frontend: { url: string, oidcButtonLabel: string, dashboardMessage: string, umami: { domains: string, scriptUrl: string, websiteId: string } }, exerciseParameters: { downloadRequired: boolean, filters: { type: Array<ExerciseLot>, level: Array<ExerciseLevel>, force: Array<ExerciseForce>, mechanic: Array<ExerciseMechanic>, equipment: Array<ExerciseEquipment>, muscle: Array<ExerciseMuscle> }, lotMapping: Array<{ lot: ExerciseLot, bests: Array<WorkoutSetPersonalBest> }> } } };
+export type CoreDetailsQuery = { coreDetails: { version: string, docsLink: string, pageSize: number, websiteUrl: string, smtpEnabled: boolean, oidcEnabled: boolean, signupAllowed: boolean, repositoryLink: string, disableTelemetry: boolean, tokenValidForDays: number, localAuthDisabled: boolean, fileStorageEnabled: boolean, peopleSearchSources: Array<MediaSource>, isServerKeyValidated: boolean, metadataGroupSourceLotMappings: Array<{ lot: MediaLot, source: MediaSource }>, metadataLotSourceMappings: Array<{ lot: MediaLot, sources: Array<MediaSource> }>, metadataProviderLanguages: Array<{ source: MediaSource, default: string, supported: Array<string> }>, frontend: { url: string, oidcButtonLabel: string, dashboardMessage: string, umami: { domains: string, scriptUrl: string, websiteId: string } }, exerciseParameters: { downloadRequired: boolean, filters: { type: Array<ExerciseLot>, level: Array<ExerciseLevel>, force: Array<ExerciseForce>, mechanic: Array<ExerciseMechanic>, equipment: Array<ExerciseEquipment>, muscle: Array<ExerciseMuscle> }, lotMapping: Array<{ lot: ExerciseLot, bests: Array<WorkoutSetPersonalBest> }> } } };
 
 export type MetadataGroupDetailsQueryVariables = Exact<{
   metadataGroupId: Scalars['String']['input'];
@@ -3743,8 +3755,8 @@ export const UpdateUserExerciseSettingsDocument = {"kind":"Document","definition
 export const MergeExerciseDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MergeExercise"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"mergeFrom"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"mergeInto"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mergeExercise"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"mergeFrom"},"value":{"kind":"Variable","name":{"kind":"Name","value":"mergeFrom"}}},{"kind":"Argument","name":{"kind":"Name","value":"mergeInto"},"value":{"kind":"Variable","name":{"kind":"Name","value":"mergeInto"}}}]}]}}]} as unknown as DocumentNode<MergeExerciseMutation, MergeExerciseMutationVariables>;
 export const RefreshUserMetadataRecommendationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RefreshUserMetadataRecommendations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"refreshUserMetadataRecommendations"}}]}}]} as unknown as DocumentNode<RefreshUserMetadataRecommendationsMutation, RefreshUserMetadataRecommendationsMutationVariables>;
 export const MarkEntityAsPartialDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MarkEntityAsPartial"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MarkEntityAsPartialInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"markEntityAsPartial"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<MarkEntityAsPartialMutation, MarkEntityAsPartialMutationVariables>;
-export const MetadataDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MetadataDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"metadataDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"metadataId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"isNsfw"}},{"kind":"Field","name":{"kind":"Name","value":"isPartial"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"suggestions"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}},{"kind":"Field","name":{"kind":"Name","value":"publishDate"}},{"kind":"Field","name":{"kind":"Name","value":"providerRating"}},{"kind":"Field","name":{"kind":"Name","value":"createdByUserId"}},{"kind":"Field","name":{"kind":"Name","value":"productionStatus"}},{"kind":"Field","name":{"kind":"Name","value":"originalLanguage"}},{"kind":"Field","name":{"kind":"Name","value":"genres"},"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"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"part"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"images"}},{"kind":"Field","name":{"kind":"Name","value":"videos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"videoId"}},{"kind":"Field","name":{"kind":"Name","value":"source"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"creators"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"character"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"watchProviders"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"languages"}}]}},{"kind":"Field","name":{"kind":"Name","value":"animeSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodes"}}]}},{"kind":"Field","name":{"kind":"Name","value":"audioBookSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}}]}},{"kind":"Field","name":{"kind":"Name","value":"bookSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pages"}}]}},{"kind":"Field","name":{"kind":"Name","value":"movieSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}}]}},{"kind":"Field","name":{"kind":"Name","value":"mangaSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"volumes"}},{"kind":"Field","name":{"kind":"Name","value":"chapters"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"overview"}},{"kind":"Field","name":{"kind":"Name","value":"thumbnail"}},{"kind":"Field","name":{"kind":"Name","value":"number"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"publishDate"}}]}},{"kind":"Field","name":{"kind":"Name","value":"totalEpisodes"}}]}},{"kind":"Field","name":{"kind":"Name","value":"showSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalSeasons"}},{"kind":"Field","name":{"kind":"Name","value":"totalEpisodes"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"seasons"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"seasonNumber"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"overview"}},{"kind":"Field","name":{"kind":"Name","value":"backdropImages"}},{"kind":"Field","name":{"kind":"Name","value":"posterImages"}},{"kind":"Field","name":{"kind":"Name","value":"episodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"posterImages"}},{"kind":"Field","name":{"kind":"Name","value":"episodeNumber"}},{"kind":"Field","name":{"kind":"Name","value":"publishDate"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"overview"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"visualNovelSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"length"}}]}},{"kind":"Field","name":{"kind":"Name","value":"videoGameSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"platforms"}}]}},{"kind":"Field","name":{"kind":"Name","value":"musicSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"viewCount"}},{"kind":"Field","name":{"kind":"Name","value":"byVariousArtists"}}]}}]}}]}}]} as unknown as DocumentNode<MetadataDetailsQuery, MetadataDetailsQueryVariables>;
-export const PersonDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PersonDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"personId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"personId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"personId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"associatedMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PersonDetailsGroupedByRolePart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"associatedMetadataGroups"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PersonDetailsGroupedByRolePart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"place"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"website"}},{"kind":"Field","name":{"kind":"Name","value":"deathDate"}},{"kind":"Field","name":{"kind":"Name","value":"birthDate"}},{"kind":"Field","name":{"kind":"Name","value":"isPartial"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"displayImages"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PersonDetailsGroupedByRolePart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PersonDetailsGroupedByRole"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"entityId"}},{"kind":"Field","name":{"kind":"Name","value":"character"}}]}}]}}]} as unknown as DocumentNode<PersonDetailsQuery, PersonDetailsQueryVariables>;
+export const MetadataDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MetadataDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"metadataDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"metadataId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"isNsfw"}},{"kind":"Field","name":{"kind":"Name","value":"isPartial"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"suggestions"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}},{"kind":"Field","name":{"kind":"Name","value":"publishDate"}},{"kind":"Field","name":{"kind":"Name","value":"providerRating"}},{"kind":"Field","name":{"kind":"Name","value":"createdByUserId"}},{"kind":"Field","name":{"kind":"Name","value":"productionStatus"}},{"kind":"Field","name":{"kind":"Name","value":"originalLanguage"}},{"kind":"Field","name":{"kind":"Name","value":"animeSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodes"}}]}},{"kind":"Field","name":{"kind":"Name","value":"audioBookSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}}]}},{"kind":"Field","name":{"kind":"Name","value":"movieSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}}]}},{"kind":"Field","name":{"kind":"Name","value":"genres"},"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"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"part"}}]}},{"kind":"Field","name":{"kind":"Name","value":"watchProviders"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"languages"}}]}},{"kind":"Field","name":{"kind":"Name","value":"bookSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pages"}},{"kind":"Field","name":{"kind":"Name","value":"isCompilation"}}]}},{"kind":"Field","name":{"kind":"Name","value":"mangaSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"volumes"}},{"kind":"Field","name":{"kind":"Name","value":"chapters"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"images"}},{"kind":"Field","name":{"kind":"Name","value":"videos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"videoId"}},{"kind":"Field","name":{"kind":"Name","value":"source"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"creators"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"character"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"overview"}},{"kind":"Field","name":{"kind":"Name","value":"thumbnail"}},{"kind":"Field","name":{"kind":"Name","value":"number"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"publishDate"}}]}},{"kind":"Field","name":{"kind":"Name","value":"totalEpisodes"}}]}},{"kind":"Field","name":{"kind":"Name","value":"showSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalSeasons"}},{"kind":"Field","name":{"kind":"Name","value":"totalEpisodes"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"seasons"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"seasonNumber"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"overview"}},{"kind":"Field","name":{"kind":"Name","value":"backdropImages"}},{"kind":"Field","name":{"kind":"Name","value":"posterImages"}},{"kind":"Field","name":{"kind":"Name","value":"episodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"posterImages"}},{"kind":"Field","name":{"kind":"Name","value":"episodeNumber"}},{"kind":"Field","name":{"kind":"Name","value":"publishDate"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"overview"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"visualNovelSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"length"}}]}},{"kind":"Field","name":{"kind":"Name","value":"videoGameSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"platforms"}}]}},{"kind":"Field","name":{"kind":"Name","value":"musicSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"viewCount"}},{"kind":"Field","name":{"kind":"Name","value":"byVariousArtists"}}]}}]}}]}}]} as unknown as DocumentNode<MetadataDetailsQuery, MetadataDetailsQueryVariables>;
+export const PersonDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PersonDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"personId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"personId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"personId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"associatedMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PersonDetailsGroupedByRolePart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"associatedMetadataGroups"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PersonDetailsGroupedByRolePart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"place"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"website"}},{"kind":"Field","name":{"kind":"Name","value":"deathDate"}},{"kind":"Field","name":{"kind":"Name","value":"birthDate"}},{"kind":"Field","name":{"kind":"Name","value":"isPartial"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"displayImages"}},{"kind":"Field","name":{"kind":"Name","value":"alternateNames"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PersonDetailsGroupedByRolePart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PersonDetailsGroupedByRole"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"entityId"}},{"kind":"Field","name":{"kind":"Name","value":"character"}}]}}]}}]} as unknown as DocumentNode<PersonDetailsQuery, PersonDetailsQueryVariables>;
 export const UserAnalyticsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserAnalytics"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UserAnalyticsInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userAnalytics"},"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":"hours"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hour"}},{"kind":"Field","name":{"kind":"Name","value":"entities"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"entityId"}},{"kind":"Field","name":{"kind":"Name","value":"entityLot"}},{"kind":"Field","name":{"kind":"Name","value":"metadataLot"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"activities"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"groupedBy"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalDuration"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"day"}},{"kind":"Field","name":{"kind":"Name","value":"bookCount"}},{"kind":"Field","name":{"kind":"Name","value":"showCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"movieCount"}},{"kind":"Field","name":{"kind":"Name","value":"musicCount"}},{"kind":"Field","name":{"kind":"Name","value":"animeCount"}},{"kind":"Field","name":{"kind":"Name","value":"mangaCount"}},{"kind":"Field","name":{"kind":"Name","value":"workoutCount"}},{"kind":"Field","name":{"kind":"Name","value":"podcastCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalBookPages"}},{"kind":"Field","name":{"kind":"Name","value":"audioBookCount"}},{"kind":"Field","name":{"kind":"Name","value":"videoGameCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"visualNovelCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutReps"}},{"kind":"Field","name":{"kind":"Name","value":"totalShowDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalMovieDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalMusicDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalMetadataCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutWeight"}},{"kind":"Field","name":{"kind":"Name","value":"totalPodcastDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutDistance"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutRestTime"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutDuration"}},{"kind":"Field","name":{"kind":"Name","value":"userMeasurementCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalVideoGameDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalAudioBookDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalPersonReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalVisualNovelDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalMetadataReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutPersonalBests"}},{"kind":"Field","name":{"kind":"Name","value":"totalCollectionReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalMetadataGroupReviewCount"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workoutReps"}},{"kind":"Field","name":{"kind":"Name","value":"workoutCount"}},{"kind":"Field","name":{"kind":"Name","value":"workoutWeight"}},{"kind":"Field","name":{"kind":"Name","value":"workoutDistance"}},{"kind":"Field","name":{"kind":"Name","value":"workoutDuration"}},{"kind":"Field","name":{"kind":"Name","value":"workoutRestTime"}},{"kind":"Field","name":{"kind":"Name","value":"measurementCount"}},{"kind":"Field","name":{"kind":"Name","value":"workoutPersonalBests"}},{"kind":"Field","name":{"kind":"Name","value":"workoutExercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"exercise"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workoutMuscles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"muscle"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workoutEquipments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"equipment"}}]}}]}}]}}]}}]} as unknown as DocumentNode<UserAnalyticsQuery, UserAnalyticsQueryVariables>;
 export const UserDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserDetails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userDetails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"isDisabled"}},{"kind":"Field","name":{"kind":"Name","value":"oidcIssuerId"}},{"kind":"Field","name":{"kind":"Name","value":"preferences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"general"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reviewScale"}},{"kind":"Field","name":{"kind":"Name","value":"gridPacking"}},{"kind":"Field","name":{"kind":"Name","value":"displayNsfw"}},{"kind":"Field","name":{"kind":"Name","value":"disableVideos"}},{"kind":"Field","name":{"kind":"Name","value":"persistQueries"}},{"kind":"Field","name":{"kind":"Name","value":"disableReviews"}},{"kind":"Field","name":{"kind":"Name","value":"disableIntegrations"}},{"kind":"Field","name":{"kind":"Name","value":"disableWatchProviders"}},{"kind":"Field","name":{"kind":"Name","value":"disableNavigationAnimation"}},{"kind":"Field","name":{"kind":"Name","value":"dashboard"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hidden"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"numElements"}},{"kind":"Field","name":{"kind":"Name","value":"deduplicateMedia"}}]}},{"kind":"Field","name":{"kind":"Name","value":"watchProviders"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"values"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logging"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"muteSounds"}},{"kind":"Field","name":{"kind":"Name","value":"promptForRestTimer"}},{"kind":"Field","name":{"kind":"Name","value":"showDetailsWhileEditing"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unitSystem"}},{"kind":"Field","name":{"kind":"Name","value":"setRestTimers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SetRestTimersPart"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"measurements"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"custom"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"dataType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"inbuilt"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"bodyMassIndex"}},{"kind":"Field","name":{"kind":"Name","value":"totalBodyWater"}},{"kind":"Field","name":{"kind":"Name","value":"muscle"}},{"kind":"Field","name":{"kind":"Name","value":"leanBodyMass"}},{"kind":"Field","name":{"kind":"Name","value":"bodyFat"}},{"kind":"Field","name":{"kind":"Name","value":"boneMass"}},{"kind":"Field","name":{"kind":"Name","value":"visceralFat"}},{"kind":"Field","name":{"kind":"Name","value":"waistCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"waistToHeightRatio"}},{"kind":"Field","name":{"kind":"Name","value":"hipCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"waistToHipRatio"}},{"kind":"Field","name":{"kind":"Name","value":"chestCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"thighCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"bicepsCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"neckCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"bodyFatCaliper"}},{"kind":"Field","name":{"kind":"Name","value":"chestSkinfold"}},{"kind":"Field","name":{"kind":"Name","value":"abdominalSkinfold"}},{"kind":"Field","name":{"kind":"Name","value":"thighSkinfold"}},{"kind":"Field","name":{"kind":"Name","value":"basalMetabolicRate"}},{"kind":"Field","name":{"kind":"Name","value":"totalDailyEnergyExpenditure"}},{"kind":"Field","name":{"kind":"Name","value":"calories"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"notifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"toSend"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"featuresEnabled"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"analytics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"others"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"calendar"}},{"kind":"Field","name":{"kind":"Name","value":"collections"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"workouts"}},{"kind":"Field","name":{"kind":"Name","value":"templates"}},{"kind":"Field","name":{"kind":"Name","value":"measurements"}}]}},{"kind":"Field","name":{"kind":"Name","value":"media"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"groups"}},{"kind":"Field","name":{"kind":"Name","value":"people"}},{"kind":"Field","name":{"kind":"Name","value":"genres"}},{"kind":"Field","name":{"kind":"Name","value":"specific"}}]}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SetRestTimersPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SetRestTimersSettings"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"drop"}},{"kind":"Field","name":{"kind":"Name","value":"warmup"}},{"kind":"Field","name":{"kind":"Name","value":"normal"}},{"kind":"Field","name":{"kind":"Name","value":"failure"}}]}}]} as unknown as DocumentNode<UserDetailsQuery, UserDetailsQueryVariables>;
 export const UserExerciseDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserExerciseDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"exerciseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userExerciseDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"exerciseId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"exerciseId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"collections"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CollectionPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"reviews"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ReviewItemPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"history"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"idx"}},{"kind":"Field","name":{"kind":"Name","value":"workoutId"}},{"kind":"Field","name":{"kind":"Name","value":"workoutEndOn"}},{"kind":"Field","name":{"kind":"Name","value":"bestSet"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetRecordPart"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"exerciseId"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedOn"}},{"kind":"Field","name":{"kind":"Name","value":"exerciseNumTimesInteracted"}},{"kind":"Field","name":{"kind":"Name","value":"exerciseExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"settings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"excludeFromAnalytics"}},{"kind":"Field","name":{"kind":"Name","value":"setRestTimers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SetRestTimersPart"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"lifetimeStats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"personalBestsAchieved"}}]}},{"kind":"Field","name":{"kind":"Name","value":"personalBests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"sets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"setIdx"}},{"kind":"Field","name":{"kind":"Name","value":"workoutId"}},{"kind":"Field","name":{"kind":"Name","value":"exerciseIdx"}}]}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenAnimeExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenMangaExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenMangaExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"volume"}},{"kind":"Field","name":{"kind":"Name","value":"chapter"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutSetStatisticPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSetStatistic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"pace"}},{"kind":"Field","name":{"kind":"Name","value":"oneRm"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"volume"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CollectionPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Collection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ReviewItemPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ReviewItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"rating"}},{"kind":"Field","name":{"kind":"Name","value":"postedOn"}},{"kind":"Field","name":{"kind":"Name","value":"isSpoiler"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"textOriginal"}},{"kind":"Field","name":{"kind":"Name","value":"textRendered"}},{"kind":"Field","name":{"kind":"Name","value":"seenItemsAssociatedWith"}},{"kind":"Field","name":{"kind":"Name","value":"postedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"likedBy"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}},{"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":"showExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"Field","name":{"kind":"Name","value":"animeExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"mangaExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenMangaExtraInformationPart"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutSetRecordPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSetRecord"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"personalBests"}},{"kind":"Field","name":{"kind":"Name","value":"statistic"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetStatisticPart"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SetRestTimersPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SetRestTimersSettings"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"drop"}},{"kind":"Field","name":{"kind":"Name","value":"warmup"}},{"kind":"Field","name":{"kind":"Name","value":"normal"}},{"kind":"Field","name":{"kind":"Name","value":"failure"}}]}}]} as unknown as DocumentNode<UserExerciseDetailsQuery, UserExerciseDetailsQueryVariables>;
@@ -3772,7 +3784,7 @@ export const ImportReportsDocument = {"kind":"Document","definitions":[{"kind":"
 export const GenresListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GenresList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SearchInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"genresList"},"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":"items"}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}}]}}]}}]}}]} as unknown as DocumentNode<GenresListQuery, GenresListQueryVariables>;
 export const GenreDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GenreDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GenreDetailsInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"genreDetails"},"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":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"numItems"}}]}},{"kind":"Field","name":{"kind":"Name","value":"contents"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}}]}},{"kind":"Field","name":{"kind":"Name","value":"items"}}]}}]}}]}}]} as unknown as DocumentNode<GenreDetailsQuery, GenreDetailsQueryVariables>;
 export const CollectionContentsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CollectionContents"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CollectionContentsInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"collectionContents"},"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":"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":"reviews"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ReviewItemPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"results"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}}]}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"entityId"}},{"kind":"Field","name":{"kind":"Name","value":"entityLot"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenAnimeExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenMangaExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenMangaExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"volume"}},{"kind":"Field","name":{"kind":"Name","value":"chapter"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ReviewItemPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ReviewItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"rating"}},{"kind":"Field","name":{"kind":"Name","value":"postedOn"}},{"kind":"Field","name":{"kind":"Name","value":"isSpoiler"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"textOriginal"}},{"kind":"Field","name":{"kind":"Name","value":"textRendered"}},{"kind":"Field","name":{"kind":"Name","value":"seenItemsAssociatedWith"}},{"kind":"Field","name":{"kind":"Name","value":"postedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"likedBy"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}},{"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":"showExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"Field","name":{"kind":"Name","value":"animeExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"mangaExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenMangaExtraInformationPart"}}]}}]}}]} as unknown as DocumentNode<CollectionContentsQuery, CollectionContentsQueryVariables>;
-export const CoreDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CoreDetails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"coreDetails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"docsLink"}},{"kind":"Field","name":{"kind":"Name","value":"pageSize"}},{"kind":"Field","name":{"kind":"Name","value":"websiteUrl"}},{"kind":"Field","name":{"kind":"Name","value":"smtpEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"oidcEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"signupAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"repositoryLink"}},{"kind":"Field","name":{"kind":"Name","value":"disableTelemetry"}},{"kind":"Field","name":{"kind":"Name","value":"tokenValidForDays"}},{"kind":"Field","name":{"kind":"Name","value":"localAuthDisabled"}},{"kind":"Field","name":{"kind":"Name","value":"fileStorageEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"isServerKeyValidated"}},{"kind":"Field","name":{"kind":"Name","value":"metadataLotSourceMappings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"sources"}}]}},{"kind":"Field","name":{"kind":"Name","value":"metadataProviderLanguages"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"default"}},{"kind":"Field","name":{"kind":"Name","value":"supported"}}]}},{"kind":"Field","name":{"kind":"Name","value":"frontend"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"oidcButtonLabel"}},{"kind":"Field","name":{"kind":"Name","value":"dashboardMessage"}},{"kind":"Field","name":{"kind":"Name","value":"umami"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domains"}},{"kind":"Field","name":{"kind":"Name","value":"scriptUrl"}},{"kind":"Field","name":{"kind":"Name","value":"websiteId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"exerciseParameters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"downloadRequired"}},{"kind":"Field","name":{"kind":"Name","value":"filters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"level"}},{"kind":"Field","name":{"kind":"Name","value":"force"}},{"kind":"Field","name":{"kind":"Name","value":"mechanic"}},{"kind":"Field","name":{"kind":"Name","value":"equipment"}},{"kind":"Field","name":{"kind":"Name","value":"muscle"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lotMapping"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"bests"}}]}}]}}]}}]}}]} as unknown as DocumentNode<CoreDetailsQuery, CoreDetailsQueryVariables>;
+export const CoreDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CoreDetails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"coreDetails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"docsLink"}},{"kind":"Field","name":{"kind":"Name","value":"pageSize"}},{"kind":"Field","name":{"kind":"Name","value":"websiteUrl"}},{"kind":"Field","name":{"kind":"Name","value":"smtpEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"oidcEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"signupAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"repositoryLink"}},{"kind":"Field","name":{"kind":"Name","value":"disableTelemetry"}},{"kind":"Field","name":{"kind":"Name","value":"tokenValidForDays"}},{"kind":"Field","name":{"kind":"Name","value":"localAuthDisabled"}},{"kind":"Field","name":{"kind":"Name","value":"fileStorageEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"peopleSearchSources"}},{"kind":"Field","name":{"kind":"Name","value":"isServerKeyValidated"}},{"kind":"Field","name":{"kind":"Name","value":"metadataGroupSourceLotMappings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"source"}}]}},{"kind":"Field","name":{"kind":"Name","value":"metadataLotSourceMappings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"sources"}}]}},{"kind":"Field","name":{"kind":"Name","value":"metadataProviderLanguages"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"default"}},{"kind":"Field","name":{"kind":"Name","value":"supported"}}]}},{"kind":"Field","name":{"kind":"Name","value":"frontend"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"oidcButtonLabel"}},{"kind":"Field","name":{"kind":"Name","value":"dashboardMessage"}},{"kind":"Field","name":{"kind":"Name","value":"umami"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domains"}},{"kind":"Field","name":{"kind":"Name","value":"scriptUrl"}},{"kind":"Field","name":{"kind":"Name","value":"websiteId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"exerciseParameters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"downloadRequired"}},{"kind":"Field","name":{"kind":"Name","value":"filters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"level"}},{"kind":"Field","name":{"kind":"Name","value":"force"}},{"kind":"Field","name":{"kind":"Name","value":"mechanic"}},{"kind":"Field","name":{"kind":"Name","value":"equipment"}},{"kind":"Field","name":{"kind":"Name","value":"muscle"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lotMapping"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"bests"}}]}}]}}]}}]}}]} as unknown as DocumentNode<CoreDetailsQuery, CoreDetailsQueryVariables>;
 export const MetadataGroupDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MetadataGroupDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"metadataGroupId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"metadataGroupDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"metadataGroupId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"metadataGroupId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"contents"}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"parts"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"isPartial"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"displayImages"}}]}}]}}]}}]} as unknown as DocumentNode<MetadataGroupDetailsQuery, MetadataGroupDetailsQueryVariables>;
 export const MetadataGroupSearchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MetadataGroupSearch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MetadataGroupSearchInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"metadataGroupSearch"},"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":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}}]}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"parts"}}]}}]}}]}}]} as unknown as DocumentNode<MetadataGroupSearchQuery, MetadataGroupSearchQueryVariables>;
 export const MetadataListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MetadataList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MetadataListInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"metadataList"},"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":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}}]}},{"kind":"Field","name":{"kind":"Name","value":"items"}}]}}]}}]} as unknown as DocumentNode<MetadataListQuery, MetadataListQueryVariables>;
diff --git a/libs/generated/src/graphql/backend/types.generated.ts b/libs/generated/src/graphql/backend/types.generated.ts
index f41e17b5c8..52c2d124b3 100644
--- a/libs/generated/src/graphql/backend/types.generated.ts
+++ b/libs/generated/src/graphql/backend/types.generated.ts
@@ -104,10 +104,12 @@ export enum BackgroundJob {
 
 export type BookSpecifics = {
   __typename?: 'BookSpecifics';
+  isCompilation?: Maybe<Scalars['Boolean']['output']>;
   pages?: Maybe<Scalars['Int']['output']>;
 };
 
 export type BookSpecificsInput = {
+  isCompilation?: InputMaybe<Scalars['Boolean']['input']>;
   pages?: InputMaybe<Scalars['Int']['input']>;
 };
 
@@ -220,10 +222,12 @@ export type CoreDetails = {
   frontend: FrontendConfig;
   isServerKeyValidated: Scalars['Boolean']['output'];
   localAuthDisabled: Scalars['Boolean']['output'];
+  metadataGroupSourceLotMappings: Array<MetadataGroupSourceLotMapping>;
   metadataLotSourceMappings: Array<MetadataLotSourceMappings>;
   metadataProviderLanguages: Array<ProviderLanguageInformation>;
   oidcEnabled: Scalars['Boolean']['output'];
   pageSize: Scalars['Int']['output'];
+  peopleSearchSources: Array<MediaSource>;
   repositoryLink: Scalars['String']['output'];
   signupAllowed: Scalars['Boolean']['output'];
   smtpEnabled: Scalars['Boolean']['output'];
@@ -1039,6 +1043,7 @@ export enum MediaSource {
   Audible = 'AUDIBLE',
   Custom = 'CUSTOM',
   GoogleBooks = 'GOOGLE_BOOKS',
+  Hardcover = 'HARDCOVER',
   Igdb = 'IGDB',
   Itunes = 'ITUNES',
   Listennotes = 'LISTENNOTES',
@@ -1104,6 +1109,12 @@ export type MetadataGroupSearchResults = {
   items: Array<MetadataGroupSearchItem>;
 };
 
+export type MetadataGroupSourceLotMapping = {
+  __typename?: 'MetadataGroupSourceLotMapping';
+  lot: MediaLot;
+  source: MediaSource;
+};
+
 export type MetadataGroupsListInput = {
   filter?: InputMaybe<MediaFilter>;
   invertCollection?: InputMaybe<Scalars['Boolean']['input']>;
@@ -1629,6 +1640,7 @@ export type PeopleSearchResults = {
 
 export type Person = {
   __typename?: 'Person';
+  alternateNames?: Maybe<Array<Scalars['String']['output']>>;
   birthDate?: Maybe<Scalars['NaiveDate']['output']>;
   createdOn: Scalars['DateTime']['output'];
   deathDate?: Maybe<Scalars['NaiveDate']['output']>;
@@ -1674,6 +1686,7 @@ export type PersonSortInput = {
 
 export type PersonSourceSpecificsInput = {
   isAnilistStudio?: InputMaybe<Scalars['Boolean']['input']>;
+  isHardcoverPublisher?: InputMaybe<Scalars['Boolean']['input']>;
   isTmdbCompany?: InputMaybe<Scalars['Boolean']['input']>;
 };
 
diff --git a/libs/graphql/src/backend/queries/MetadataDetails.gql b/libs/graphql/src/backend/queries/MetadataDetails.gql
index 0d012f2919..4a25f98d65 100644
--- a/libs/graphql/src/backend/queries/MetadataDetails.gql
+++ b/libs/graphql/src/backend/queries/MetadataDetails.gql
@@ -16,6 +16,15 @@ query MetadataDetails($metadataId: String!) {
 		createdByUserId
 		productionStatus
 		originalLanguage
+		animeSpecifics {
+			episodes
+		}
+		audioBookSpecifics {
+			runtime
+		}
+		movieSpecifics {
+			runtime
+		}
 		genres {
 			id
 			name
@@ -25,6 +34,19 @@ query MetadataDetails($metadataId: String!) {
 			name
 			part
 		}
+		watchProviders {
+			name
+			image
+			languages
+		}
+		bookSpecifics {
+			pages
+			isCompilation
+		}
+		mangaSpecifics {
+			volumes
+			chapters
+		}
 		assets {
 			images
 			videos {
@@ -41,27 +63,6 @@ query MetadataDetails($metadataId: String!) {
 				character
 			}
 		}
-		watchProviders {
-			name
-			image
-			languages
-		}
-		animeSpecifics {
-			episodes
-		}
-		audioBookSpecifics {
-			runtime
-		}
-		bookSpecifics {
-			pages
-		}
-		movieSpecifics {
-			runtime
-		}
-		mangaSpecifics {
-			volumes
-			chapters
-		}
 		podcastSpecifics {
 			episodes {
 				id
diff --git a/libs/graphql/src/backend/queries/PersonDetails.gql b/libs/graphql/src/backend/queries/PersonDetails.gql
index 2b766771be..9855d1eef6 100644
--- a/libs/graphql/src/backend/queries/PersonDetails.gql
+++ b/libs/graphql/src/backend/queries/PersonDetails.gql
@@ -20,6 +20,7 @@ query PersonDetails($personId: String!) {
 			identifier
 			description
 			displayImages
+			alternateNames
 		}
 	}
 }
diff --git a/libs/graphql/src/backend/queries/combined.gql b/libs/graphql/src/backend/queries/combined.gql
index cbca79ac1c..2833447805 100644
--- a/libs/graphql/src/backend/queries/combined.gql
+++ b/libs/graphql/src/backend/queries/combined.gql
@@ -269,7 +269,12 @@ query CoreDetails {
 		tokenValidForDays
 		localAuthDisabled
 		fileStorageEnabled
+		peopleSearchSources
 		isServerKeyValidated
+		metadataGroupSourceLotMappings {
+			lot
+			source
+		}
 		metadataLotSourceMappings {
 			lot
 			sources


Test patch: