Repository: IgnisDa/ryot (language: rust)

Issue:
Fitness statistics
![image](https://github.com/user-attachments/assets/94d6787f-3189-4798-ba32-588d11011eac)
![image](https://github.com/user-attachments/assets/f4eeb36d-fc26-4cef-9593-c7ace50e8df1)



Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 2491c94eca..beb9fee3a2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -837,13 +837,11 @@ dependencies = [
  "axum",
  "background",
  "cache-service",
- "chrono",
  "chrono-tz",
  "collection-resolver",
  "collection-service",
  "common-utils",
  "config",
- "database-models",
  "dependent-models",
  "dotenvy",
  "env-utils",
@@ -853,6 +851,7 @@ dependencies = [
  "file-storage-service",
  "fitness-resolver",
  "fitness-service",
+ "futures",
  "http 1.1.0",
  "importer-resolver",
  "importer-service",
@@ -869,9 +868,7 @@ dependencies = [
  "schematic",
  "sea-orm",
  "sea-orm-migration",
- "serde",
  "serde_json",
- "serde_with 3.11.0",
  "statistics-resolver",
  "statistics-service",
  "supporting-service",
@@ -880,7 +877,7 @@ dependencies = [
  "tower-http",
  "tracing",
  "tracing-subscriber",
- "unkey",
+ "traits",
  "user-resolver",
  "user-service",
 ]
@@ -1280,6 +1277,7 @@ dependencies = [
  "database-models",
  "database-utils",
  "dependent-models",
+ "dependent-utils",
  "enums",
  "media-models",
  "migrations",
@@ -1326,6 +1324,7 @@ name = "common-models"
 version = "0.1.0"
 dependencies = [
  "async-graphql",
+ "chrono",
  "educe",
  "enum_meta",
  "enums",
@@ -1741,7 +1740,6 @@ dependencies = [
 name = "dependent-utils"
 version = "0.1.0"
 dependencies = [
- "anyhow",
  "application-utils",
  "async-graphql",
  "background",
@@ -3331,7 +3329,6 @@ dependencies = [
  "indexmap 2.7.0",
  "itertools 0.13.0",
  "media-models",
- "nanoid",
  "providers",
  "reqwest 0.12.9",
  "rust_decimal",
@@ -3713,7 +3710,6 @@ dependencies = [
  "serde",
  "serde_json",
  "serde_with 3.11.0",
- "strum",
 ]
 
 [[package]]
@@ -3840,11 +3836,13 @@ dependencies = [
  "sea-query",
  "serde",
  "serde_json",
+ "serde_with 3.11.0",
  "slug",
  "supporting-service",
  "tokio",
  "tracing",
  "traits",
+ "unkey",
  "user-models",
  "uuid",
 ]
@@ -4577,7 +4575,6 @@ dependencies = [
  "application-utils",
  "async-trait",
  "chrono",
- "chrono-tz",
  "common-models",
  "common-utils",
  "config",
@@ -4604,6 +4601,7 @@ dependencies = [
  "serde_json",
  "serde_with 3.11.0",
  "strum",
+ "supporting-service",
  "tracing",
  "traits",
 ]
@@ -5831,8 +5829,11 @@ dependencies = [
 name = "specific-models"
 version = "0.1.0"
 dependencies = [
+ "common-models",
  "rust_decimal",
+ "sea-orm",
  "serde",
+ "serde_with 3.11.0",
  "strum",
 ]
 
@@ -6131,8 +6132,7 @@ name = "statistics-resolver"
 version = "0.1.0"
 dependencies = [
  "async-graphql",
- "dependent-models",
- "media-models",
+ "common-models",
  "statistics-service",
  "traits",
 ]
@@ -6142,12 +6142,15 @@ name = "statistics-service"
 version = "0.1.0"
 dependencies = [
  "async-graphql",
+ "common-models",
  "database-models",
  "database-utils",
- "dependent-models",
- "media-models",
+ "enums",
+ "hashbag",
+ "itertools 0.13.0",
  "sea-orm",
  "supporting-service",
+ "tokio",
  "tracing",
 ]
 
@@ -6243,6 +6246,7 @@ dependencies = [
  "background",
  "cache-service",
  "chrono-tz",
+ "common-models",
  "config",
  "file-storage-service",
  "openidconnect",
@@ -6952,6 +6956,7 @@ dependencies = [
  "dependent-models",
  "media-models",
  "traits",
+ "user-models",
  "user-service",
 ]
 
diff --git a/apps/backend/Cargo.toml b/apps/backend/Cargo.toml
index 8c9074914e..e83be9b007 100644
--- a/apps/backend/Cargo.toml
+++ b/apps/backend/Cargo.toml
@@ -15,13 +15,11 @@ axum = { workspace = true }
 aws-sdk-s3 = { workspace = true }
 background = { path = "../../crates/background" }
 cache-service = { path = "../../crates/services/cache" }
-chrono = { workspace = true }
 chrono-tz = { workspace = true }
 collection-resolver = { path = "../../crates/resolvers/collection" }
 collection-service = { path = "../../crates/services/collection" }
 common-utils = { path = "../../crates/utils/common" }
 config = { path = "../../crates/config" }
-database-models = { path = "../../crates/models/database" }
 dependent-models = { path = "../../crates/models/dependent" }
 dotenvy = { workspace = true }
 env-utils = { path = "../../crates/utils/env" }
@@ -31,6 +29,7 @@ file-storage-resolver = { path = "../../crates/resolvers/file-storage" }
 file-storage-service = { path = "../../crates/services/file-storage" }
 fitness-resolver = { path = "../../crates/resolvers/fitness" }
 fitness-service = { path = "../../crates/services/fitness" }
+futures = { workspace = true }
 http = { workspace = true }
 importer-resolver = { path = "../../crates/resolvers/importer" }
 importer-service = { path = "../../crates/services/importer" }
@@ -46,9 +45,7 @@ reqwest = { workspace = true }
 router-resolver = { path = "../../crates/resolvers/router" }
 sea-orm = { workspace = true }
 sea-orm-migration = { workspace = true }
-serde = { workspace = true }
 serde_json = { workspace = true }
-serde_with = { workspace = true }
 schematic = { workspace = true }
 statistics-resolver = { path = "../../crates/resolvers/statistics" }
 statistics-service = { path = "../../crates/services/statistics" }
@@ -58,6 +55,6 @@ tower = { workspace = true }
 tower-http = { workspace = true }
 tracing = { workspace = true }
 tracing-subscriber = { workspace = true }
-unkey = { workspace = true }
+traits = { path = "../../crates/traits" }
 user-resolver = { path = "../../crates/resolvers/user" }
 user-service = { path = "../../crates/services/user" }
diff --git a/apps/backend/src/common.rs b/apps/backend/src/common.rs
index b9693896c8..824b9fb401 100644
--- a/apps/backend/src/common.rs
+++ b/apps/backend/src/common.rs
@@ -19,8 +19,8 @@ use exporter_resolver::{ExporterMutation, ExporterQuery};
 use exporter_service::ExporterService;
 use file_storage_resolver::{FileStorageMutation, FileStorageQuery};
 use file_storage_service::FileStorageService;
-use fitness_resolver::{ExerciseMutation, ExerciseQuery};
-use fitness_service::ExerciseService;
+use fitness_resolver::{FitnessMutation, FitnessQuery};
+use fitness_service::FitnessService;
 use importer_resolver::{ImporterMutation, ImporterQuery};
 use importer_service::ImporterService;
 use integration_service::IntegrationService;
@@ -49,7 +49,7 @@ pub struct AppServices {
     pub app_router: Router,
     pub importer_service: Arc<ImporterService>,
     pub exporter_service: Arc<ExporterService>,
-    pub exercise_service: Arc<ExerciseService>,
+    pub fitness_service: Arc<FitnessService>,
     pub statistics_service: Arc<StatisticsService>,
     pub integration_service: Arc<IntegrationService>,
     pub miscellaneous_service: Arc<MiscellaneousService>,
@@ -57,7 +57,6 @@ pub struct AppServices {
 
 #[allow(clippy::too_many_arguments)]
 pub async fn create_app_services(
-    is_pro: bool,
     db: DatabaseConnection,
     timezone: chrono_tz::Tz,
     s3_client: aws_sdk_s3::Client,
@@ -73,7 +72,6 @@ pub async fn create_app_services(
     let cache_service = CacheService::new(&db);
     let supporting_service = Arc::new(
         SupportingService::new(
-            is_pro,
             &db,
             timezone,
             cache_service,
@@ -87,7 +85,7 @@ pub async fn create_app_services(
     );
     let user_service = Arc::new(UserService(supporting_service.clone()));
     let importer_service = Arc::new(ImporterService(supporting_service.clone()));
-    let exercise_service = Arc::new(ExerciseService(supporting_service.clone()));
+    let fitness_service = Arc::new(FitnessService(supporting_service.clone()));
     let exporter_service = Arc::new(ExporterService(supporting_service.clone()));
     let collection_service = Arc::new(CollectionService(supporting_service.clone()));
     let statistics_service = Arc::new(StatisticsService(supporting_service.clone()));
@@ -104,7 +102,7 @@ pub async fn create_app_services(
     .data(user_service.clone())
     .data(importer_service.clone())
     .data(exporter_service.clone())
-    .data(exercise_service.clone())
+    .data(fitness_service.clone())
     .data(statistics_service.clone())
     .data(collection_service.clone())
     .data(file_storage_service.clone())
@@ -149,7 +147,7 @@ pub async fn create_app_services(
         app_router,
         importer_service,
         exporter_service,
-        exercise_service,
+        fitness_service,
         statistics_service,
         integration_service,
         miscellaneous_service,
@@ -192,7 +190,7 @@ pub struct QueryRoot(
     MiscellaneousQuery,
     ImporterQuery,
     ExporterQuery,
-    ExerciseQuery,
+    FitnessQuery,
     FileStorageQuery,
     StatisticsQuery,
     CollectionQuery,
@@ -204,7 +202,7 @@ pub struct MutationRoot(
     MiscellaneousMutation,
     ImporterMutation,
     ExporterMutation,
-    ExerciseMutation,
+    FitnessMutation,
     FileStorageMutation,
     CollectionMutation,
     UserMutation,
diff --git a/apps/backend/src/job.rs b/apps/backend/src/job.rs
index 194f2bedf1..528b7dd3c0 100644
--- a/apps/backend/src/job.rs
+++ b/apps/backend/src/job.rs
@@ -4,27 +4,41 @@ use apalis::prelude::*;
 use background::{ApplicationJob, CoreApplicationJob, ScheduledJob};
 use common_utils::ryot_log;
 use exporter_service::ExporterService;
-use fitness_service::ExerciseService;
+use fitness_service::FitnessService;
 use importer_service::ImporterService;
 use integration_service::IntegrationService;
 use media_models::CommitMediaInput;
 use miscellaneous_service::MiscellaneousService;
 use statistics_service::StatisticsService;
+use traits::TraceOk;
 
-pub async fn background_jobs(
+pub async fn run_background_jobs(
     information: ScheduledJob,
     misc_service: Data<Arc<MiscellaneousService>>,
 ) -> Result<(), Error> {
     ryot_log!(debug, "Running job at {:#?}", information.0);
-    misc_service.perform_background_jobs().await.unwrap();
+    misc_service.perform_background_jobs().await.trace_ok();
     Ok(())
 }
 
-pub async fn sync_integrations_data(
+pub async fn run_frequent_jobs(
     _information: ScheduledJob,
+    fitness_service: Data<Arc<FitnessService>>,
+    misc_service: Data<Arc<MiscellaneousService>>,
     integration_service: Data<Arc<IntegrationService>>,
 ) -> Result<(), Error> {
-    integration_service.yank_integrations_data().await.unwrap();
+    misc_service
+        .perform_server_key_validation()
+        .await
+        .trace_ok();
+    integration_service
+        .yank_integrations_data()
+        .await
+        .trace_ok();
+    fitness_service
+        .process_users_scheduled_for_workout_revision()
+        .await
+        .trace_ok();
     Ok(())
 }
 
@@ -67,7 +81,7 @@ pub async fn perform_application_job(
     integration_service: Data<Arc<IntegrationService>>,
     importer_service: Data<Arc<ImporterService>>,
     exporter_service: Data<Arc<ExporterService>>,
-    exercise_service: Data<Arc<ExerciseService>>,
+    fitness_service: Data<Arc<FitnessService>>,
     statistics_service: Data<Arc<StatisticsService>>,
 ) -> Result<(), Error> {
     let name = information.to_string();
@@ -84,10 +98,9 @@ pub async fn perform_application_job(
                 .await
                 .is_ok()
         }
-        ApplicationJob::ReEvaluateUserWorkouts(user_id) => exercise_service
-            .re_evaluate_user_workouts(user_id)
-            .await
-            .is_ok(),
+        ApplicationJob::ReviseUserWorkouts(user_id) => {
+            fitness_service.revise_user_workouts(user_id).await.is_ok()
+        }
         ApplicationJob::UpdateMetadata(metadata_id, force_update) => misc_service
             .update_metadata_and_notify_users(&metadata_id, force_update)
             .await
@@ -104,7 +117,7 @@ pub async fn perform_application_job(
             .update_metadata_group(&metadata_group_id)
             .await
             .is_ok(),
-        ApplicationJob::UpdateGithubExerciseJob(exercise) => exercise_service
+        ApplicationJob::UpdateGithubExerciseJob(exercise) => fitness_service
             .update_github_exercise(exercise)
             .await
             .is_ok(),
@@ -126,12 +139,22 @@ pub async fn perform_application_job(
         ApplicationJob::PerformExport(user_id) => {
             exporter_service.perform_export(user_id).await.is_ok()
         }
-        ApplicationJob::UpdateExerciseLibrary => exercise_service
+        ApplicationJob::UpdateExerciseLibrary => fitness_service
             .deploy_update_exercise_library_job()
             .await
             .is_ok(),
         ApplicationJob::SyncIntegrationsData => {
-            integration_service.yank_integrations_data().await.is_ok()
+            integration_service
+                .yank_integrations_data()
+                .await
+                .trace_ok();
+            integration_service
+                .sync_integrations_data_to_owned_collection()
+                .await
+                .is_ok()
+        }
+        ApplicationJob::PerformServerKeyValidation => {
+            misc_service.perform_server_key_validation().await.is_ok()
         }
         ApplicationJob::HandleEntityAddedToCollectionEvent(collection_to_entity_id) => {
             integration_service
diff --git a/apps/backend/src/main.rs b/apps/backend/src/main.rs
index 0f4862e651..be68b012ac 100644
--- a/apps/backend/src/main.rs
+++ b/apps/backend/src/main.rs
@@ -18,16 +18,13 @@ use apalis::{
 };
 use aws_sdk_s3::config::Region;
 use background::ApplicationJob;
-use chrono::{DateTime, NaiveDate, TimeZone, Utc};
-use common_utils::{convert_naive_to_utc, ryot_log, COMPILATION_TIMESTAMP, PROJECT_NAME, TEMP_DIR};
-use database_models::prelude::Exercise;
-use env_utils::{APP_VERSION, UNKEY_API_ID};
+use common_utils::{ryot_log, PROJECT_NAME, TEMP_DIR};
+use env_utils::APP_VERSION;
+use futures::future::join_all;
 use logs_wheel::LogFileInitializer;
 use migrations::Migrator;
-use sea_orm::{ConnectionTrait, Database, DatabaseConnection, EntityTrait, PaginatorTrait};
+use sea_orm::{ConnectionTrait, Database, DatabaseConnection};
 use sea_orm_migration::MigratorTrait;
-use serde::{Deserialize, Serialize};
-use serde_with::skip_serializing_none;
 use tokio::{
     join,
     net::TcpListener,
@@ -35,13 +32,12 @@ use tokio::{
 };
 use tower::buffer::BufferLayer;
 use tracing_subscriber::{fmt, layer::SubscriberExt};
-use unkey::{models::VerifyKeyRequest, Client};
 
 use crate::{
     common::create_app_services,
     job::{
-        background_jobs, perform_application_job, perform_core_application_job,
-        sync_integrations_data,
+        perform_application_job, perform_core_application_job, run_background_jobs,
+        run_frequent_jobs,
     },
 };
 
@@ -70,7 +66,7 @@ async fn main() -> Result<()> {
     let config = Arc::new(config::load_app_config()?);
     if config.server.sleep_before_startup_seconds > 0 {
         let duration = TokioDuration::from_secs(config.server.sleep_before_startup_seconds);
-        ryot_log!(info, "Sleeping for {:?} before starting up...", duration);
+        ryot_log!(warn, "Sleeping for {:?} before starting up...", duration);
         sleep(duration).await;
     }
 
@@ -78,14 +74,8 @@ async fn main() -> Result<()> {
     let sync_every_minutes = config.integration.sync_every_minutes;
     let disable_background_jobs = config.server.disable_background_jobs;
 
-    let compile_timestamp = Utc.timestamp_opt(COMPILATION_TIMESTAMP, 0).unwrap();
-    ryot_log!(debug, "Compiled at: {}", compile_timestamp);
-
     let config_dump_path = PathBuf::new().join(TEMP_DIR).join("config.json");
     fs::write(config_dump_path, serde_json::to_string_pretty(&config)?)?;
-    let is_pro = get_is_pro(&config.server.pro_key, &compile_timestamp).await;
-
-    ryot_log!(debug, "Is pro: {:#?}", is_pro);
 
     let mut aws_conf = aws_sdk_s3::Config::builder()
         .region(Region::new(config.file_storage.s3_region.clone()))
@@ -129,24 +119,17 @@ async fn main() -> Result<()> {
         .unwrap_or_else(|_| chrono_tz::Etc::GMT);
     ryot_log!(info, "Timezone: {}", tz);
 
-    perform_application_job_storage
-        .enqueue(ApplicationJob::SyncIntegrationsData)
-        .await
-        .unwrap();
-
-    if Exercise::find().count(&db).await? == 0 {
-        ryot_log!(
-            info,
-            "Instance does not have exercises data. Deploying job to download them..."
-        );
-        perform_application_job_storage
-            .enqueue(ApplicationJob::UpdateExerciseLibrary)
-            .await
-            .unwrap();
-    }
+    join_all(
+        [
+            ApplicationJob::PerformServerKeyValidation,
+            ApplicationJob::SyncIntegrationsData,
+            ApplicationJob::UpdateExerciseLibrary,
+        ]
+        .map(|j| perform_application_job_storage.enqueue(j)),
+    )
+    .await;
 
     let app_services = create_app_services(
-        is_pro,
         db,
         tz,
         s3_client,
@@ -196,9 +179,10 @@ async fn main() -> Result<()> {
     let listener = TcpListener::bind(format!("{host}:{port}")).await.unwrap();
     ryot_log!(info, "Listening on: {}", listener.local_addr()?);
 
+    let fitness_service_1 = app_services.fitness_service.clone();
+    let fitness_service_2 = app_services.fitness_service.clone();
     let importer_service_1 = app_services.importer_service.clone();
     let exporter_service_1 = app_services.exporter_service.clone();
-    let exercise_service_1 = app_services.exercise_service.clone();
     let statistics_service_1 = app_services.statistics_service.clone();
     let integration_service_1 = app_services.integration_service.clone();
     let integration_service_2 = app_services.integration_service.clone();
@@ -206,11 +190,12 @@ async fn main() -> Result<()> {
     let miscellaneous_service_1 = app_services.miscellaneous_service.clone();
     let miscellaneous_service_2 = app_services.miscellaneous_service.clone();
     let miscellaneous_service_3 = app_services.miscellaneous_service.clone();
+    let miscellaneous_service_4 = app_services.miscellaneous_service.clone();
 
     let monitor = Monitor::<TokioExecutor>::new()
         .register_with_count(
             1,
-            WorkerBuilder::new("background_jobs")
+            WorkerBuilder::new("daily_background_jobs")
                 .stream(
                     // every day
                     CronStream::new_with_timezone(Schedule::from_str("0 0 0 * * *").unwrap(), tz)
@@ -218,11 +203,11 @@ async fn main() -> Result<()> {
                 )
                 .layer(ApalisTraceLayer::new())
                 .data(miscellaneous_service_1.clone())
-                .build_fn(background_jobs),
+                .build_fn(run_background_jobs),
         )
         .register_with_count(
             1,
-            WorkerBuilder::new("sync_integrations_data")
+            WorkerBuilder::new("frequent_jobs")
                 .stream(
                     CronStream::new_with_timezone(
                         Schedule::from_str(&format!("0 */{} * * * *", sync_every_minutes)).unwrap(),
@@ -232,7 +217,9 @@ async fn main() -> Result<()> {
                 )
                 .layer(ApalisTraceLayer::new())
                 .data(integration_service_1.clone())
-                .build_fn(sync_integrations_data),
+                .data(fitness_service_2.clone())
+                .data(miscellaneous_service_4.clone())
+                .build_fn(run_frequent_jobs),
         )
         // application jobs
         .register_with_count(
@@ -247,7 +234,7 @@ async fn main() -> Result<()> {
         .register_with_count(
             3,
             WorkerBuilder::new("perform_application_job")
-                .data(exercise_service_1.clone())
+                .data(fitness_service_1.clone())
                 .data(exporter_service_1.clone())
                 .data(importer_service_1.clone())
                 .data(statistics_service_1.clone())
@@ -354,47 +341,3 @@ END $$;
     .await?;
     Ok(())
 }
-
-async fn get_is_pro(pro_key: &str, compilation_time: &DateTime<Utc>) -> bool {
-    if pro_key.is_empty() {
-        return false;
-    }
-
-    ryot_log!(debug, "Verifying pro key for API ID: {:#?}", UNKEY_API_ID);
-
-    #[skip_serializing_none]
-    #[derive(Debug, Serialize, Clone, Deserialize)]
-    struct Meta {
-        expiry: Option<NaiveDate>,
-    }
-
-    let unkey_client = Client::new("public");
-    let verify_request = VerifyKeyRequest::new(pro_key, UNKEY_API_ID);
-    let validated_key = match unkey_client.verify_key(verify_request).await {
-        Ok(verify_response) => {
-            if !verify_response.valid {
-                ryot_log!(debug, "Pro key is no longer valid.");
-                return false;
-            }
-            verify_response
-        }
-        Err(verify_error) => {
-            ryot_log!(debug, "Pro key verification error: {:?}", verify_error);
-            return false;
-        }
-    };
-    let key_meta = validated_key
-        .meta
-        .map(|meta| serde_json::from_value::<Meta>(meta).unwrap());
-    ryot_log!(debug, "Expiry: {:?}", key_meta.clone().map(|m| m.expiry));
-    if let Some(meta) = key_meta {
-        if let Some(expiry) = meta.expiry {
-            if compilation_time > &convert_naive_to_utc(expiry) {
-                ryot_log!(warn, "Pro key has expired. Please renew your subscription.");
-                return false;
-            }
-        }
-    }
-    ryot_log!(info, "Pro key verified successfully");
-    true
-}
diff --git a/apps/frontend/app/components/common.tsx b/apps/frontend/app/components/common.tsx
index f39b0f8218..1f2fb8cd69 100644
--- a/apps/frontend/app/components/common.tsx
+++ b/apps/frontend/app/components/common.tsx
@@ -59,6 +59,7 @@ import {
 	IconTrash,
 	IconX,
 } from "@tabler/icons-react";
+import clsx from "clsx";
 import Cookies from "js-cookie";
 import type { ReactNode, Ref } from "react";
 import { useState } from "react";
@@ -233,7 +234,7 @@ export const DebouncedSearchInput = (props: {
 export const ProRequiredAlert = (props: { tooltipLabel?: string }) => {
 	const coreDetails = useCoreDetails();
 
-	return !coreDetails.isPro ? (
+	return !coreDetails.isServerKeyValidated ? (
 		<Alert>
 			<Tooltip label={props.tooltipLabel} disabled={!props.tooltipLabel}>
 				<Text size="xs">{PRO_REQUIRED_MESSAGE}</Text>
@@ -255,6 +256,7 @@ export const BaseMediaDisplayItem = (props: {
 	isLoading: boolean;
 	nameRight?: ReactNode;
 	imageUrl?: string | null;
+	highlightImage?: boolean;
 	innerRef?: Ref<HTMLDivElement>;
 	labels?: { right?: ReactNode; left?: ReactNode };
 	onImageClickBehavior: string | (() => Promise<void>);
@@ -276,8 +278,8 @@ export const BaseMediaDisplayItem = (props: {
 			<Box onClick={props.onImageClickBehavior}>{iProps.children}</Box>
 		);
 	const defaultOverlayProps = {
-		style: { zIndex: 10, ...blackBgStyles },
 		pos: "absolute",
+		style: { zIndex: 10, ...blackBgStyles },
 	} as const;
 
 	return (
@@ -285,9 +287,17 @@ export const BaseMediaDisplayItem = (props: {
 			<Box pos="relative" w="100%">
 				<SurroundingElement>
 					<Tooltip label={props.name} position="top">
-						<Paper style={{ overflow: "hidden" }} pos="relative" radius="md">
+						<Paper
+							radius="md"
+							pos="relative"
+							style={{ overflow: "hidden" }}
+							className={clsx({
+								[classes.highlightImage]: props.highlightImage,
+							})}
+						>
 							<Image
 								src={props.imageUrl}
+								alt={`Image for ${props.name}`}
 								style={{
 									cursor: "pointer",
 									...match(gridPacking)
@@ -295,8 +305,6 @@ export const BaseMediaDisplayItem = (props: {
 										.with(GridPacking.Dense, () => ({ height: 180 }))
 										.exhaustive(),
 								}}
-								alt={`Image for ${props.name}`}
-								className={classes.mediaImage}
 								styles={{
 									root: {
 										transitionProperty: "transform",
@@ -361,9 +369,9 @@ export const BaseMediaDisplayItem = (props: {
 				</>
 			) : (
 				<Flex
+					mt={2}
 					w="100%"
 					direction="column"
-					mt={2}
 					px={match(gridPacking)
 						.with(GridPacking.Normal, () => ({ base: 6, md: 3 }))
 						.with(GridPacking.Dense, () => ({ md: 2 }))
@@ -394,11 +402,11 @@ export const BaseMediaDisplayItem = (props: {
 };
 
 export const FiltersModal = (props: {
+	title?: string;
 	opened: boolean;
 	cookieName: string;
 	children: ReactNode;
 	closeFiltersModal: () => void;
-	title?: string;
 }) => {
 	const navigate = useNavigate();
 
@@ -467,8 +475,8 @@ export const CollectionsFilter = (props: {
 };
 
 export const DisplayThreePointReview = (props: {
-	rating?: string | null;
 	size?: number;
+	rating?: string | null;
 }) =>
 	match(convertDecimalToThreePointSmiley(Number(props.rating || "")))
 		.with(ThreePointSmileyRating.Happy, () => (
@@ -838,10 +846,10 @@ export const DisplayCollectionEntity = (props: {
 		.run();
 
 export const DisplayCollection = (props: {
-	creatorUserId: string;
-	col: { id: string; name: string };
 	entityId: string;
 	entityLot: EntityLot;
+	creatorUserId: string;
+	col: { id: string; name: string };
 }) => {
 	const color = useGetRandomMantineColor(props.col.name);
 	const submit = useConfirmSubmit();
diff --git a/apps/frontend/app/components/fitness.tsx b/apps/frontend/app/components/fitness.tsx
index 23a81120d0..1c7ada6ddf 100644
--- a/apps/frontend/app/components/fitness.tsx
+++ b/apps/frontend/app/components/fitness.tsx
@@ -242,7 +242,7 @@ export const ExerciseHistory = (props: {
 	const exercise =
 		workoutDetails?.details.information.exercises[props.exerciseIdx];
 	const { data: exerciseDetails } = useQuery(
-		getExerciseDetailsQuery(exercise?.name || ""),
+		getExerciseDetailsQuery(exercise?.id || ""),
 	);
 	const isInSuperset = props.supersetInformation?.find((s) =>
 		s.exercises.includes(props.exerciseIdx),
@@ -276,7 +276,7 @@ export const ExerciseHistory = (props: {
 													}),
 													props.exerciseIdx.toString(),
 												)
-											: getExerciseDetailsPath(exercise.name)
+											: getExerciseDetailsPath(exercise.id)
 									}
 									fw="bold"
 									lineClamp={1}
@@ -284,7 +284,7 @@ export const ExerciseHistory = (props: {
 								>
 									{props.hideExerciseDetails
 										? workoutDetails.details.name
-										: exercise.name}
+										: exerciseDetails.name}
 								</Anchor>
 								{!props.hideExtraDetailsButton ? (
 									<ActionIcon onClick={toggle} variant="transparent">
@@ -425,7 +425,7 @@ export const ExerciseDisplayItem = (props: {
 	return (
 		<BaseMediaDisplayItem
 			innerRef={ref}
-			name={exerciseDetails?.id}
+			name={exerciseDetails?.name}
 			isLoading={isExerciseDetailsLoading}
 			imageUrl={exerciseDetails?.attributes.images.at(0)}
 			onImageClickBehavior={getExerciseDetailsPath(props.exerciseId)}
diff --git a/apps/frontend/app/components/media.tsx b/apps/frontend/app/components/media.tsx
index 51a1571774..e0b4b9e832 100644
--- a/apps/frontend/app/components/media.tsx
+++ b/apps/frontend/app/components/media.tsx
@@ -19,6 +19,8 @@ import {
 	MetadataGroupDetailsDocument,
 	PersonDetailsDocument,
 	SeenState,
+	UserMetadataGroupDetailsDocument,
+	UserPersonDetailsDocument,
 	UserReviewScale,
 	UserToMediaReason,
 } from "@ryot/generated/graphql/backend/graphql";
@@ -48,6 +50,7 @@ import {
 } from "~/lib/generals";
 import {
 	useConfirmSubmit,
+	useMetadataDetails,
 	useUserDetails,
 	useUserMetadataDetails,
 	useUserPreferences,
@@ -119,10 +122,7 @@ export const MetadataDisplayItem = (props: {
 	const userPreferences = useUserPreferences();
 	const { ref, inViewport } = useInViewport();
 	const { data: metadataDetails, isLoading: isMetadataDetailsLoading } =
-		useQuery({
-			...getPartialMetadataDetailsQuery(props.metadataId),
-			enabled: inViewport,
-		});
+		useMetadataDetails(props.metadataId, inViewport);
 	const { data: userMetadataDetails } = useUserMetadataDetails(
 		props.metadataId,
 		inViewport,
@@ -156,11 +156,12 @@ export const MetadataDisplayItem = (props: {
 	return (
 		<BaseMediaDisplayItem
 			innerRef={ref}
-			progress={currentProgress}
 			altName={props.altName}
-			imageUrl={metadataDetails?.image}
+			progress={currentProgress}
 			isLoading={isMetadataDetailsLoading}
 			name={props.name ?? metadataDetails?.title}
+			imageUrl={metadataDetails?.assets.images.at(0)}
+			highlightImage={userMetadataDetails?.recentlyConsumed}
 			onImageClickBehavior={$path("/media/item/:id", { id: props.metadataId })}
 			labels={
 				metadataDetails
@@ -284,12 +285,24 @@ export const MetadataGroupDisplayItem = (props: {
 			},
 			enabled: inViewport,
 		});
+	const { data: userMetadataGroupDetails } = useQuery({
+		queryKey: queryFactory.media.userMetadataGroupDetails(props.metadataGroupId)
+			.queryKey,
+		queryFn: async () => {
+			return clientGqlService
+				.request(UserMetadataGroupDetailsDocument, props)
+				.then((data) => data.userMetadataGroupDetails);
+		},
+		enabled: inViewport,
+	});
 
 	return (
 		<BaseMediaDisplayItem
 			innerRef={ref}
-			name={metadataDetails?.details.title}
 			isLoading={isMetadataDetailsLoading}
+			name={metadataDetails?.details.title}
+			imageOverlay={{ topRight: props.topRight }}
+			highlightImage={userMetadataGroupDetails?.recentlyConsumed}
 			onImageClickBehavior={$path("/media/groups/item/:id", {
 				id: props.metadataGroupId,
 			})}
@@ -307,7 +320,6 @@ export const MetadataGroupDisplayItem = (props: {
 						}
 					: undefined
 			}
-			imageOverlay={{ topRight: props.topRight }}
 		/>
 	);
 };
@@ -327,23 +339,33 @@ export const PersonDisplayItem = (props: {
 		},
 		enabled: inViewport,
 	});
+	const { data: userPersonDetails } = useQuery({
+		queryKey: queryFactory.media.userPersonDetails(props.personId).queryKey,
+		queryFn: async () => {
+			return clientGqlService
+				.request(UserPersonDetailsDocument, props)
+				.then((data) => data.userPersonDetails);
+		},
+		enabled: inViewport,
+	});
 
 	return (
 		<BaseMediaDisplayItem
 			innerRef={ref}
 			name={personDetails?.details.name}
 			isLoading={isPersonDetailsLoading}
+			imageOverlay={{ topRight: props.topRight }}
+			highlightImage={userPersonDetails?.recentlyConsumed}
+			imageUrl={personDetails?.details.displayImages.at(0)}
 			onImageClickBehavior={$path("/media/people/item/:id", {
 				id: props.personId,
 			})}
-			imageUrl={personDetails?.details.displayImages.at(0)}
 			labels={{
 				left: personDetails
 					? `${personDetails.contents.reduce((sum, content) => sum + content.items.length, 0)} items`
 					: undefined,
 				right: props.rightLabel,
 			}}
-			imageOverlay={{ topRight: props.topRight }}
 		/>
 	);
 };
diff --git a/apps/frontend/app/lib/generals.ts b/apps/frontend/app/lib/generals.ts
index dea2c05a70..703de1a364 100644
--- a/apps/frontend/app/lib/generals.ts
+++ b/apps/frontend/app/lib/generals.ts
@@ -3,12 +3,14 @@ import {
 	createQueryKeys,
 	mergeQueryKeys,
 } from "@lukemorales/query-key-factory";
+import type { MantineColor } from "@mantine/core";
 import {
 	MediaLot,
 	MediaSource,
 	MetadataDetailsDocument,
 	MetadataPartialDetailsDocument,
 	SetLot,
+	type UserAnalyticsQueryVariables,
 	UserMetadataDetailsDocument,
 } from "@ryot/generated/graphql/backend/graphql";
 import { inRange, isString } from "@ryot/ts-utils";
@@ -297,6 +299,11 @@ export const getStringAsciiValue = (input: string) => {
 	return total;
 };
 
+export const selectRandomElement = <T>(array: T[], input: string): T => {
+	// taken from https://stackoverflow.com/questions/44975435/using-mod-operator-in-javascript-to-wrap-around#comment76926119_44975435
+	return array[(getStringAsciiValue(input) + array.length) % array.length];
+};
+
 export const getMetadataIcon = (lot: MediaLot) =>
 	match(lot)
 		.with(MediaLot.Book, () => IconBook)
@@ -353,9 +360,15 @@ const mediaQueryKeys = createQueryKeys("media", {
 	metadataGroupDetails: (metadataGroupId: string) => ({
 		queryKey: ["metadataGroupDetails", metadataGroupId],
 	}),
+	userMetadataGroupDetails: (metadataGroupId: string) => ({
+		queryKey: ["userMetadataGroupDetails", metadataGroupId],
+	}),
 	personDetails: (personId: string) => ({
 		queryKey: ["personDetails", personId],
 	}),
+	userPersonDetails: (personId: string) => ({
+		queryKey: ["userPersonDetails", personId],
+	}),
 	genreImages: (genreId: string) => ({
 		queryKey: ["genreDetails", "images", genreId],
 	}),
@@ -392,16 +405,20 @@ const miscellaneousQueryKeys = createQueryKeys("miscellaneous", {
 	coreDetails: () => ({
 		queryKey: ["coreDetails"],
 	}),
-	dailyUserActivities: (startDate?: string, endDate?: string) => ({
-		queryKey: ["dailyUserActivities", startDate, endDate],
+});
+
+const analyticsQueryKeys = createQueryKeys("analytics", {
+	user: (input: UserAnalyticsQueryVariables) => ({
+		queryKey: ["user", input],
 	}),
 });
 
 export const queryFactory = mergeQueryKeys(
 	usersQueryKeys,
 	mediaQueryKeys,
-	collectionQueryKeys,
 	fitnessQueryKeys,
+	analyticsQueryKeys,
+	collectionQueryKeys,
 	miscellaneousQueryKeys,
 );
 
@@ -451,7 +468,34 @@ export const refreshUserMetadataDetails = (metadataId: string) =>
 		});
 	}, 1500);
 
+export const convertUtcHourToLocalHour = (
+	utcHour: number,
+	userTimezone?: string,
+) => {
+	const targetTimezone = userTimezone || dayjs.tz.guess();
+	const utcDate = dayjs.utc().hour(utcHour).minute(0).second(0);
+	const localDate = utcDate.tz(targetTimezone);
+	return localDate.hour();
+};
+
 export const getExerciseDetailsPath = (exerciseId: string) =>
 	$path("/fitness/exercises/item/:id", {
 		id: encodeURIComponent(exerciseId),
 	});
+
+type EntityColor = Record<MediaLot | (string & {}), MantineColor>;
+
+export const MediaColors: EntityColor = {
+	ANIME: "blue",
+	AUDIO_BOOK: "orange",
+	BOOK: "lime",
+	MANGA: "purple",
+	MOVIE: "cyan",
+	PODCAST: "yellow",
+	SHOW: "red",
+	VISUAL_NOVEL: "pink",
+	VIDEO_GAME: "teal",
+	WORKOUT: "violet",
+	REVIEW: "green.5",
+	USER_MEASUREMENT: "indigo",
+};
diff --git a/apps/frontend/app/lib/hooks.ts b/apps/frontend/app/lib/hooks.ts
index b2cec590da..b49371d6b9 100644
--- a/apps/frontend/app/lib/hooks.ts
+++ b/apps/frontend/app/lib/hooks.ts
@@ -20,8 +20,8 @@ import {
 	FitnessAction,
 	dayjsLib,
 	getMetadataDetailsQuery,
-	getStringAsciiValue,
 	getUserMetadataDetailsQuery,
+	selectRandomElement,
 } from "~/lib/generals";
 import { type InProgressWorkout, useCurrentWorkout } from "~/lib/state/fitness";
 import type { loader as dashboardLoader } from "~/routes/_dashboard";
@@ -34,9 +34,7 @@ export const useGetMantineColors = () => {
 
 export const useGetRandomMantineColor = (input: string) => {
 	const colors = useGetMantineColors();
-
-	// taken from https://stackoverflow.com/questions/44975435/using-mod-operator-in-javascript-to-wrap-around#comment76926119_44975435
-	return colors[(getStringAsciiValue(input) + colors.length) % colors.length];
+	return selectRandomElement(colors, input);
 };
 
 export const useFallbackImageUrl = (text = "No Image") => {
@@ -107,8 +105,11 @@ export const useGetWorkoutStarter = () => {
 	return fn;
 };
 
-export const useMetadataDetails = (metadataId?: string | null) => {
-	return useQuery(getMetadataDetailsQuery(metadataId));
+export const useMetadataDetails = (
+	metadataId?: string | null,
+	enabled?: boolean,
+) => {
+	return useQuery({ ...getMetadataDetailsQuery(metadataId), enabled });
 };
 
 export const useUserMetadataDetails = (
@@ -138,7 +139,7 @@ export const useUserUnitSystem = () =>
 	useUserPreferences().fitness.exercises.unitSystem;
 
 export const useApplicationEvents = () => {
-	const { version, isPro } = useCoreDetails();
+	const { version, isServerKeyValidated: isPro } = useCoreDetails();
 
 	const sendEvent = (eventName: string, data: Record<string, unknown>) => {
 		window.umami?.track(eventName, { isPro, version, ...data });
diff --git a/apps/frontend/app/lib/state/fitness.ts b/apps/frontend/app/lib/state/fitness.ts
index b6a65c1c14..75d3ac7930 100644
--- a/apps/frontend/app/lib/state/fitness.ts
+++ b/apps/frontend/app/lib/state/fitness.ts
@@ -48,6 +48,7 @@ type AlreadyDoneExerciseSet = Pick<ExerciseSet, "statistic">;
 type Media = { imageSrc: string; key: string };
 
 export type Exercise = {
+	name: string;
 	lot: ExerciseLot;
 	identifier: string;
 	exerciseId: string;
@@ -57,6 +58,7 @@ export type Exercise = {
 	isCollapsed?: boolean;
 	sets: Array<ExerciseSet>;
 	isShowDetailsOpen: boolean;
+	scrollMarginRemoved?: true;
 	openedDetailsTab?: "images" | "history";
 	alreadyDoneSets: Array<AlreadyDoneExerciseSet>;
 };
@@ -292,20 +294,21 @@ export const duplicateOldWorkout = async (
 				params.updateWorkoutId ? v.confirmedAt : undefined,
 			),
 		);
-		const exerciseDetails = await getExerciseDetails(ex.name);
+		const exerciseDetails = await getExerciseDetails(ex.id);
 		inProgress.exercises.push({
 			identifier: randomUUID(),
+			name: exerciseDetails.details.name,
 			isShowDetailsOpen: userFitnessPreferences.logging.showDetailsWhileEditing
 				? exerciseIdx === 0
 				: false,
 			images: [],
 			videos: [],
 			alreadyDoneSets: sets.map((s) => ({ statistic: s.statistic })),
-			exerciseId: ex.name,
+			exerciseId: ex.id,
 			lot: ex.lot,
 			notes: ex.notes,
 			sets: sets,
-			openedDetailsTab: !coreDetails.isPro
+			openedDetailsTab: !coreDetails.isServerKeyValidated
 				? "images"
 				: (exerciseDetails.userDetails.history?.length || 0) > 0
 					? "history"
@@ -383,6 +386,7 @@ export const addExerciseToWorkout = async (
 		}
 		draft.exercises.push({
 			identifier: randomUUID(),
+			name: exerciseDetails.details.name,
 			isShowDetailsOpen: userFitnessPreferences.logging.showDetailsWhileEditing,
 			exerciseId: ex.name,
 			lot: ex.lot,
diff --git a/apps/frontend/app/lib/utilities.server.ts b/apps/frontend/app/lib/utilities.server.ts
index 024ae6bacf..b23f134d56 100644
--- a/apps/frontend/app/lib/utilities.server.ts
+++ b/apps/frontend/app/lib/utilities.server.ts
@@ -177,6 +177,7 @@ export const getDecodedJwt = (request: Request) => {
 export const getCachedCoreDetails = async () => {
 	return await queryClient.ensureQueryData({
 		queryKey: queryFactory.miscellaneous.coreDetails().queryKey,
+		staleTime: dayjsLib.duration({ minutes: 5 }).asMilliseconds(),
 		queryFn: () =>
 			serverGqlService.request(CoreDetailsDocument).then((d) => d.coreDetails),
 	});
diff --git a/apps/frontend/app/routes/_dashboard._index.tsx b/apps/frontend/app/routes/_dashboard._index.tsx
index e655a0b6e6..329426b0c2 100644
--- a/apps/frontend/app/routes/_dashboard._index.tsx
+++ b/apps/frontend/app/routes/_dashboard._index.tsx
@@ -1,46 +1,34 @@
-import { BarChart } from "@mantine/charts";
 import {
 	Alert,
 	Box,
 	Center,
 	Container,
 	Flex,
-	LoadingOverlay,
-	type MantineColor,
-	Paper,
 	RingProgress,
-	Select,
 	SimpleGrid,
 	Stack,
 	Text,
 	Title,
 	useMantineTheme,
 } from "@mantine/core";
-import { useInViewport } from "@mantine/hooks";
 import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
 import { Link, useLoaderData } from "@remix-run/react";
 import {
 	type CalendarEventPartFragment,
 	CollectionContentsDocument,
-	DailyUserActivitiesDocument,
 	DailyUserActivitiesResponseGroupedBy,
 	DashboardElementLot,
 	GraphqlSortOrder,
-	LatestUserSummaryDocument,
 	MediaLot,
+	UserAnalyticsDocument,
 	type UserPreferences,
 	UserRecommendationsDocument,
 	UserUpcomingCalendarEventsDocument,
 } from "@ryot/generated/graphql/backend/graphql";
 import {
-	changeCase,
-	formatDateToNaiveDate,
+	formatQuantityWithCompactNotation,
 	humanizeDuration,
-	isBoolean,
 	isNumber,
-	mapValues,
-	pickBy,
-	snakeCase,
 } from "@ryot/ts-utils";
 import {
 	IconBarbell,
@@ -49,9 +37,8 @@ import {
 	IconScaleOutline,
 	IconServer,
 } from "@tabler/icons-react";
-import { useQuery } from "@tanstack/react-query";
 import CryptoJS from "crypto-js";
-import { Fragment, type ReactNode, useMemo } from "react";
+import { Fragment, type ReactNode } from "react";
 import { $path } from "remix-routes";
 import { ClientOnly } from "remix-utils/client-only";
 import invariant from "tiny-invariant";
@@ -61,15 +48,7 @@ import { ApplicationGrid, ProRequiredAlert } from "~/components/common";
 import { DisplayCollectionEntity } from "~/components/common";
 import { displayWeightWithUnit } from "~/components/fitness";
 import { MetadataDisplayItem } from "~/components/media";
-import {
-	TimeSpan,
-	clientGqlService,
-	dayjsLib,
-	getDateFromTimeSpan,
-	getLot,
-	getMetadataIcon,
-	queryFactory,
-} from "~/lib/generals";
+import { MediaColors, dayjsLib, getLot, getMetadataIcon } from "~/lib/generals";
 import {
 	useCoreDetails,
 	useGetMantineColors,
@@ -117,7 +96,7 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
 		{ collectionContents: inProgressCollectionContents },
 		userRecommendations,
 		{ userUpcomingCalendarEvents },
-		{ latestUserSummary },
+		{ userAnalytics },
 	] = await Promise.all([
 		serverGqlService.authenticatedRequest(request, CollectionContentsDocument, {
 			input: {
@@ -132,14 +111,15 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
 			UserUpcomingCalendarEventsDocument,
 			{ input: { nextMedia: takeUpcoming } },
 		),
-		serverGqlService.authenticatedRequest(
-			request,
-			LatestUserSummaryDocument,
-			undefined,
-		),
+		serverGqlService.authenticatedRequest(request, UserAnalyticsDocument, {
+			input: {
+				dateRange: {},
+				groupBy: DailyUserActivitiesResponseGroupedBy.Millennium,
+			},
+		}),
 	]);
 	return {
-		latestUserSummary,
+		userAnalytics,
 		userUpcomingCalendarEvents,
 		inProgressCollectionContents,
 		userRecommendations,
@@ -150,30 +130,13 @@ export const meta = (_args: MetaArgs<typeof loader>) => {
 	return [{ title: "Home | Ryot" }];
 };
 
-type EntityColor = Record<MediaLot | (string & {}), MantineColor>;
-
-const MediaColors: EntityColor = {
-	ANIME: "blue",
-	AUDIO_BOOK: "orange",
-	BOOK: "lime",
-	MANGA: "purple",
-	MOVIE: "cyan",
-	PODCAST: "yellow",
-	SHOW: "red",
-	VISUAL_NOVEL: "pink",
-	VIDEO_GAME: "teal",
-	WORKOUT: "violet",
-	MEASUREMENT: "indigo",
-	REVIEW: "green.5",
-};
-
 export default function Page() {
 	const loaderData = useLoaderData<typeof loader>();
 	const coreDetails = useCoreDetails();
 	const userPreferences = useUserPreferences();
 	const unitSystem = useUserUnitSystem();
 	const theme = useMantineTheme();
-	const latestUserSummary = loaderData.latestUserSummary;
+	const latestUserSummary = loaderData.userAnalytics.activities.items.at(0);
 
 	const dashboardMessage = coreDetails.frontend.dashboardMessage;
 
@@ -235,7 +198,7 @@ export default function Page() {
 						.with([DashboardElementLot.Recommendations, false], ([v, _]) => (
 							<Section key={v} lot={v}>
 								<Title>Recommendations</Title>
-								{coreDetails.isPro ? (
+								{coreDetails.isServerKeyValidated ? (
 									<ApplicationGrid>
 										{loaderData.userRecommendations.map((lm) => (
 											<MetadataDisplayItem key={lm} metadataId={lm} />
@@ -246,241 +209,238 @@ export default function Page() {
 								)}
 							</Section>
 						))
-						.with([DashboardElementLot.Activity, false], ([v, _]) => (
-							<Section key={v} lot={v}>
-								<Title>Activity</Title>
-								<ActivitySection />
-							</Section>
-						))
-						.with([DashboardElementLot.Summary, false], ([v, _]) => (
-							<Section key={v} lot={v}>
-								<Title>Summary</Title>
-								<SimpleGrid
-									cols={{ base: 1, sm: 2, md: 3 }}
-									style={{ alignItems: "center" }}
-									spacing="xs"
-								>
-									<DisplayStatForMediaType
-										lot={MediaLot.Movie}
-										data={[
-											{
-												label: "Movies",
-												value: latestUserSummary.movieCount,
-												type: "number",
-											},
-											{
-												label: "Runtime",
-												value: latestUserSummary.totalMovieDuration,
-												type: "duration",
-											},
-										]}
-									/>
-									<DisplayStatForMediaType
-										lot={MediaLot.Show}
-										data={[
-											{
-												label: "Show episodes",
-												value: latestUserSummary.showCount,
-												type: "number",
-											},
-											{
-												label: "Runtime",
-												value: latestUserSummary.totalShowDuration,
-												type: "duration",
-											},
-										]}
-									/>
-									<DisplayStatForMediaType
-										lot={MediaLot.VideoGame}
-										data={[
-											{
-												label: "Video games",
-												value: latestUserSummary.videoGameCount,
-												type: "number",
-											},
-											{
-												label: "Runtime",
-												value: latestUserSummary.totalVideoGameDuration,
-												type: "duration",
-												hideIfZero: true,
-											},
-										]}
-									/>
-									<DisplayStatForMediaType
-										lot={MediaLot.VisualNovel}
-										data={[
-											{
-												label: "Visual Novels",
-												value: latestUserSummary.visualNovelCount,
-												type: "number",
-											},
-											{
-												label: "Runtime",
-												value: latestUserSummary.totalVisualNovelDuration,
-												type: "duration",
-											},
-										]}
-									/>
-									<DisplayStatForMediaType
-										lot={MediaLot.AudioBook}
-										data={[
-											{
-												label: "Audio books",
-												value: latestUserSummary.audioBookCount,
-												type: "number",
-											},
-											{
-												label: "Runtime",
-												value: latestUserSummary.totalAudioBookDuration,
-												type: "duration",
-											},
-										]}
-									/>
-									<DisplayStatForMediaType
-										lot={MediaLot.Book}
-										data={[
-											{
-												label: "Books",
-												value: latestUserSummary.bookCount,
-												type: "number",
-											},
-											{
-												label: "Pages",
-												value: latestUserSummary.totalBookPages,
-												type: "number",
-											},
-										]}
-									/>
-									<DisplayStatForMediaType
-										lot={MediaLot.Podcast}
-										data={[
-											{
-												label: "Podcasts",
-												value: latestUserSummary.podcastCount,
-												type: "number",
-											},
-											{
-												label: "Runtime",
-												value: latestUserSummary.totalPodcastDuration,
-												type: "duration",
-											},
-										]}
-									/>
-									<DisplayStatForMediaType
-										lot={MediaLot.Manga}
-										data={[
-											{
-												label: "Manga",
-												value: latestUserSummary.mangaCount,
-												type: "number",
-											},
-										]}
-									/>
-									<DisplayStatForMediaType
-										lot={MediaLot.Anime}
-										data={[
-											{
-												label: "Anime",
-												value: latestUserSummary.animeCount,
-												type: "number",
-											},
-										]}
-									/>
-									{userPreferences.featuresEnabled.media.enabled ? (
-										<>
+						.with([DashboardElementLot.Summary, false], ([v, _]) =>
+							latestUserSummary ? (
+								<Section key={v} lot={v}>
+									<Title>Summary</Title>
+									<SimpleGrid
+										cols={{ base: 1, sm: 2, md: 3 }}
+										style={{ alignItems: "center" }}
+										spacing="xs"
+									>
+										<DisplayStatForMediaType
+											lot={MediaLot.Movie}
+											data={[
+												{
+													label: "Movies",
+													value: latestUserSummary.movieCount,
+													type: "number",
+												},
+												{
+													label: "Runtime",
+													value: latestUserSummary.totalMovieDuration,
+													type: "duration",
+												},
+											]}
+										/>
+										<DisplayStatForMediaType
+											lot={MediaLot.Show}
+											data={[
+												{
+													label: "Show episodes",
+													value: latestUserSummary.showCount,
+													type: "number",
+												},
+												{
+													label: "Runtime",
+													value: latestUserSummary.totalShowDuration,
+													type: "duration",
+												},
+											]}
+										/>
+										<DisplayStatForMediaType
+											lot={MediaLot.VideoGame}
+											data={[
+												{
+													label: "Video games",
+													value: latestUserSummary.videoGameCount,
+													type: "number",
+												},
+												{
+													label: "Runtime",
+													value: latestUserSummary.totalVideoGameDuration,
+													type: "duration",
+													hideIfZero: true,
+												},
+											]}
+										/>
+										<DisplayStatForMediaType
+											lot={MediaLot.VisualNovel}
+											data={[
+												{
+													label: "Visual Novels",
+													value: latestUserSummary.visualNovelCount,
+													type: "number",
+												},
+												{
+													label: "Runtime",
+													value: latestUserSummary.totalVisualNovelDuration,
+													type: "duration",
+												},
+											]}
+										/>
+										<DisplayStatForMediaType
+											lot={MediaLot.AudioBook}
+											data={[
+												{
+													label: "Audio books",
+													value: latestUserSummary.audioBookCount,
+													type: "number",
+												},
+												{
+													label: "Runtime",
+													value: latestUserSummary.totalAudioBookDuration,
+													type: "duration",
+												},
+											]}
+										/>
+										<DisplayStatForMediaType
+											lot={MediaLot.Book}
+											data={[
+												{
+													label: "Books",
+													value: latestUserSummary.bookCount,
+													type: "number",
+												},
+												{
+													label: "Pages",
+													value: latestUserSummary.totalBookPages,
+													type: "number",
+												},
+											]}
+										/>
+										<DisplayStatForMediaType
+											lot={MediaLot.Podcast}
+											data={[
+												{
+													label: "Podcasts",
+													value: latestUserSummary.podcastCount,
+													type: "number",
+												},
+												{
+													label: "Runtime",
+													value: latestUserSummary.totalPodcastDuration,
+													type: "duration",
+												},
+											]}
+										/>
+										<DisplayStatForMediaType
+											lot={MediaLot.Manga}
+											data={[
+												{
+													label: "Manga",
+													value: latestUserSummary.mangaCount,
+													type: "number",
+												},
+											]}
+										/>
+										<DisplayStatForMediaType
+											lot={MediaLot.Anime}
+											data={[
+												{
+													label: "Anime",
+													value: latestUserSummary.animeCount,
+													type: "number",
+												},
+											]}
+										/>
+										{userPreferences.featuresEnabled.media.enabled ? (
+											<>
+												<ActualDisplayStat
+													icon={<IconServer />}
+													lot="Metadata stats"
+													color={theme.colors.grape[8]}
+													data={[
+														{
+															label: "Media",
+															value: latestUserSummary.totalMetadataCount,
+															type: "number",
+														},
+														{
+															label: "Reviews",
+															value: latestUserSummary.totalMetadataReviewCount,
+															type: "number",
+															hideIfZero: true,
+														},
+													]}
+												/>
+												{userPreferences.featuresEnabled.media.people ? (
+													<UnstyledLink
+														to={$path("/media/people/:action", {
+															action: "list",
+														})}
+													>
+														<ActualDisplayStat
+															icon={<IconFriends />}
+															lot="People stats"
+															color={theme.colors.red[9]}
+															data={[
+																{
+																	label: "People Reviewed",
+																	value:
+																		latestUserSummary.totalPersonReviewCount,
+																	type: "number",
+																	hideIfZero: true,
+																},
+															]}
+														/>
+													</UnstyledLink>
+												) : null}
+											</>
+										) : null}
+										{userPreferences.featuresEnabled.fitness.enabled ? (
+											<UnstyledLink
+												to={$path("/fitness/:entity/list", {
+													entity: "workouts",
+												})}
+											>
+												<ActualDisplayStat
+													icon={<IconBarbell stroke={1.3} />}
+													lot="Workouts"
+													color={theme.colors.teal[2]}
+													data={[
+														{
+															label: "Workouts",
+															value: latestUserSummary.workoutCount,
+															type: "number",
+														},
+														{
+															label: "Runtime",
+															value: latestUserSummary.totalWorkoutDuration,
+															type: "duration",
+														},
+														{
+															label: "Runtime",
+															value: displayWeightWithUnit(
+																unitSystem,
+																latestUserSummary.totalWorkoutWeight,
+																true,
+															),
+															type: "string",
+														},
+													]}
+												/>
+											</UnstyledLink>
+										) : null}
+										{userPreferences.featuresEnabled.fitness.enabled ? (
 											<ActualDisplayStat
-												icon={<IconServer />}
-												lot="Metadata stats"
-												color={theme.colors.grape[8]}
+												icon={<IconScaleOutline stroke={1.3} />}
+												lot="Fitness"
+												color={theme.colors.yellow[5]}
 												data={[
 													{
-														label: "Media",
-														value: latestUserSummary.totalMetadataCount,
-														type: "number",
-													},
-													{
-														label: "Reviews",
-														value: latestUserSummary.totalMetadataReviewCount,
+														label: "Measurements",
+														value: latestUserSummary.userMeasurementCount,
 														type: "number",
 														hideIfZero: true,
 													},
 												]}
 											/>
-											{userPreferences.featuresEnabled.media.people ? (
-												<UnstyledLink
-													to={$path("/media/people/:action", {
-														action: "list",
-													})}
-												>
-													<ActualDisplayStat
-														icon={<IconFriends />}
-														lot="People stats"
-														color={theme.colors.red[9]}
-														data={[
-															{
-																label: "People Reviewed",
-																value: latestUserSummary.totalPersonReviewCount,
-																type: "number",
-																hideIfZero: true,
-															},
-														]}
-													/>
-												</UnstyledLink>
-											) : null}
-										</>
-									) : null}
-									{userPreferences.featuresEnabled.fitness.enabled ? (
-										<UnstyledLink
-											to={$path("/fitness/:entity/list", {
-												entity: "workouts",
-											})}
-										>
-											<ActualDisplayStat
-												icon={<IconBarbell stroke={1.3} />}
-												lot="Workouts"
-												color={theme.colors.teal[2]}
-												data={[
-													{
-														label: "Workouts",
-														value: latestUserSummary.workoutCount,
-														type: "number",
-													},
-													{
-														label: "Runtime",
-														value: latestUserSummary.totalWorkoutDuration,
-														type: "duration",
-													},
-													{
-														label: "Runtime",
-														value: displayWeightWithUnit(
-															unitSystem,
-															latestUserSummary.totalWorkoutWeight,
-															true,
-														),
-														type: "string",
-													},
-												]}
-											/>
-										</UnstyledLink>
-									) : null}
-									{userPreferences.featuresEnabled.fitness.enabled ? (
-										<ActualDisplayStat
-											icon={<IconScaleOutline stroke={1.3} />}
-											lot="Fitness"
-											color={theme.colors.yellow[5]}
-											data={[
-												{
-													label: "Measurements",
-													value: latestUserSummary.measurementCount,
-													type: "number",
-													hideIfZero: true,
-												},
-											]}
-										/>
-									) : null}
-								</SimpleGrid>
-							</Section>
-						))
+										) : null}
+									</SimpleGrid>
+								</Section>
+							) : null,
+						)
 						.otherwise(() => undefined),
 				)}
 			</Stack>
@@ -564,9 +524,7 @@ const ActualDisplayStat = (props: {
 											),
 										)
 										.with("number", () =>
-											new Intl.NumberFormat("en-US", {
-												notation: "compact",
-											}).format(Number(d.value)),
+											formatQuantityWithCompactNotation(Number(d.value)),
 										)
 										.exhaustive()}
 								</Text>
@@ -637,147 +595,3 @@ const UnstyledLink = (props: { children: ReactNode; to: string }) => {
 		</Link>
 	);
 };
-
-const ActivitySection = () => {
-	const { ref, inViewport } = useInViewport();
-	const [timeSpan, setTimeSpan] = useLocalStorage(
-		"ActivitySectionTimeSpan",
-		TimeSpan.Last7Days,
-	);
-	const { startDate, endDate } = useMemo(() => {
-		const now = dayjsLib();
-		const end = now.endOf("day");
-		const startDate = getDateFromTimeSpan(timeSpan);
-		return {
-			startDate: startDate
-				? formatDateToNaiveDate(startDate.toDate())
-				: undefined,
-			endDate: formatDateToNaiveDate(end.toDate()),
-		};
-	}, [timeSpan]);
-	const { data: dailyUserActivitiesData } = useQuery({
-		queryKey: queryFactory.miscellaneous.dailyUserActivities(startDate, endDate)
-			.queryKey,
-		enabled: inViewport,
-		queryFn: async () => {
-			const { dailyUserActivities } = await clientGqlService.request(
-				DailyUserActivitiesDocument,
-				{ input: { startDate, endDate } },
-			);
-			const trackSeries = mapValues(MediaColors, () => false);
-			const data = dailyUserActivities.items.map((d) => {
-				const data = Object.entries(d)
-					.filter(([_, value]) => value !== 0)
-					.map(([key, value]) => ({
-						[snakeCase(
-							key.replace("Count", "").replace("total", ""),
-						).toUpperCase()]: value,
-					}))
-					.reduce(Object.assign, {});
-				for (const key in data)
-					if (isBoolean(trackSeries[key])) trackSeries[key] = true;
-				return data;
-			});
-			const series = pickBy(trackSeries);
-			return {
-				data,
-				series,
-				groupedBy: dailyUserActivities.groupedBy,
-				totalCount: dailyUserActivities.totalCount,
-				totalDuration: dailyUserActivities.totalDuration,
-			};
-		},
-	});
-	const items = dailyUserActivitiesData?.totalCount || 0;
-
-	return (
-		<Stack ref={ref} pos="relative" h={{ base: 500, md: 400 }}>
-			<LoadingOverlay
-				visible={!dailyUserActivitiesData}
-				zIndex={1000}
-				overlayProps={{ radius: "md", blur: 3 }}
-			/>
-			<SimpleGrid cols={{ base: 2, md: 3 }} mx={{ md: "xl" }}>
-				<DisplayStat
-					label="Total"
-					value={`${new Intl.NumberFormat("en-US", {
-						notation: "compact",
-					}).format(Number(items))} items`}
-				/>
-				<DisplayStat
-					label="Duration"
-					value={
-						dailyUserActivitiesData
-							? humanizeDuration(
-									dayjsLib
-										.duration(dailyUserActivitiesData.totalDuration, "minutes")
-										.asMilliseconds(),
-									{ largest: 2 },
-								)
-							: "N/A"
-					}
-				/>
-				<Select
-					label="Time span"
-					defaultValue={timeSpan}
-					labelProps={{ c: "dimmed" }}
-					data={Object.values(TimeSpan)}
-					onChange={(v) => {
-						if (v) setTimeSpan(v as TimeSpan);
-					}}
-				/>
-			</SimpleGrid>
-			{dailyUserActivitiesData && dailyUserActivitiesData.totalCount !== 0 ? (
-				<BarChart
-					h="100%"
-					ml={-15}
-					withLegend
-					tickLine="x"
-					dataKey="DAY"
-					type="stacked"
-					data={dailyUserActivitiesData.data}
-					legendProps={{ verticalAlign: "bottom" }}
-					series={Object.keys(dailyUserActivitiesData.series).map((lot) => ({
-						name: lot,
-						color: MediaColors[lot],
-						label: changeCase(lot),
-					}))}
-					xAxisProps={{
-						tickFormatter: (v) =>
-							dayjsLib(v).format(
-								match(dailyUserActivitiesData.groupedBy)
-									.with(DailyUserActivitiesResponseGroupedBy.Day, () => "MMM D")
-									.with(DailyUserActivitiesResponseGroupedBy.Month, () => "MMM")
-									.with(
-										DailyUserActivitiesResponseGroupedBy.Year,
-										DailyUserActivitiesResponseGroupedBy.Millennium,
-										() => "YYYY",
-									)
-									.exhaustive(),
-							),
-					}}
-				/>
-			) : (
-				<Paper withBorder h="100%" w="100%" display="flex">
-					<Text m="auto" ta="center">
-						No activity found in the selected period
-					</Text>
-				</Paper>
-			)}
-		</Stack>
-	);
-};
-
-const DisplayStat = (props: {
-	label: string;
-	value: string | number;
-}) => {
-	return (
-		<Stack gap={4}>
-			<Text c="dimmed">{props.label}</Text>
-			<Text size="xl" fw="bolder">
-				{props.value}
-			</Text>
-		</Stack>
-	);
-};
diff --git a/apps/frontend/app/routes/_dashboard.analytics.tsx b/apps/frontend/app/routes/_dashboard.analytics.tsx
new file mode 100644
index 0000000000..2f99c66c1d
--- /dev/null
+++ b/apps/frontend/app/routes/_dashboard.analytics.tsx
@@ -0,0 +1,778 @@
+import { BarChart, PieChart, RadarChart } from "@mantine/charts";
+import {
+	Button,
+	Center,
+	Container,
+	Flex,
+	Grid,
+	Group,
+	Loader,
+	Menu,
+	Modal,
+	NumberInput,
+	Paper,
+	SimpleGrid,
+	Stack,
+	Text,
+} from "@mantine/core";
+import { DatePicker } from "@mantine/dates";
+import { notifications } from "@mantine/notifications";
+import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
+import { useLoaderData } from "@remix-run/react";
+import {
+	DailyUserActivitiesResponseGroupedBy,
+	UserAnalyticsDocument,
+	UserAnalyticsParametersDocument,
+	type UserAnalyticsQuery,
+} from "@ryot/generated/graphql/backend/graphql";
+import {
+	changeCase,
+	formatDateToNaiveDate,
+	formatQuantityWithCompactNotation,
+	humanizeDuration,
+	isBoolean,
+	mapValues,
+	pickBy,
+	snakeCase,
+	sum,
+} from "@ryot/ts-utils";
+import {
+	IconBarbell,
+	IconClock,
+	IconDeviceFloppy,
+	IconImageInPicture,
+	type IconProps,
+	IconRepeat,
+	IconRoad,
+	IconRulerMeasure,
+	IconStretching,
+	IconTrophy,
+	IconWeight,
+	IconZzz,
+} from "@tabler/icons-react";
+import { useQuery } from "@tanstack/react-query";
+import html2canvas from "html2canvas";
+import { produce } from "immer";
+import { atom, useAtom } from "jotai";
+import { type ComponentType, type ReactNode, useRef, useState } from "react";
+import { ClientOnly } from "remix-utils/client-only";
+import { match } from "ts-pattern";
+import { useLocalStorage } from "usehooks-ts";
+import { ProRequiredAlert } from "~/components/common";
+import {
+	displayDistanceWithUnit,
+	displayWeightWithUnit,
+} from "~/components/fitness";
+import {
+	MediaColors,
+	PRO_REQUIRED_MESSAGE,
+	clientGqlService,
+	convertUtcHourToLocalHour,
+	dayjsLib,
+	queryFactory,
+	selectRandomElement,
+} from "~/lib/generals";
+import {
+	useCoreDetails,
+	useGetMantineColors,
+	useUserPreferences,
+	useUserUnitSystem,
+} from "~/lib/hooks";
+import { serverGqlService } from "~/lib/utilities.server";
+
+enum AnalyticsTimeRanges {
+	Yesterday = "Yesterday",
+	Past7Days = "Past 7 Days",
+	Past30Days = "Past 30 Days",
+	Past6Months = "Past 6 Months",
+	Past12Months = "Past 12 Months",
+	ThisWeek = "This Week",
+	ThisMonth = "This Month",
+	ThisYear = "This Year",
+	AllTime = "All Time",
+	Custom = "Custom",
+}
+
+export type TimeSpanSettings = {
+	endDate?: string;
+	startDate?: string;
+	range: AnalyticsTimeRanges;
+};
+
+export const loader = async ({ request }: LoaderFunctionArgs) => {
+	const { userAnalyticsParameters } =
+		await serverGqlService.authenticatedRequest(
+			request,
+			UserAnalyticsParametersDocument,
+		);
+	return { userAnalyticsParameters };
+};
+
+export const meta = (_args: MetaArgs<typeof loader>) => {
+	return [{ title: "Fitness Analytics | Ryot" }];
+};
+
+const useTimeSpanSettings = () => {
+	const loaderData = useLoaderData<typeof loader>();
+	const [timeSpanSettings, setTimeSpanSettings] =
+		useLocalStorage<TimeSpanSettings>("TimeSpanSettings", {
+			range: AnalyticsTimeRanges.Past30Days,
+		});
+	const getStartTime = (range: AnalyticsTimeRanges) =>
+		match(range)
+			.with(AnalyticsTimeRanges.Yesterday, () => dayjsLib().subtract(1, "day"))
+			.with(AnalyticsTimeRanges.ThisWeek, () => dayjsLib().startOf("week"))
+			.with(AnalyticsTimeRanges.ThisMonth, () => dayjsLib().startOf("month"))
+			.with(AnalyticsTimeRanges.ThisYear, () => dayjsLib().startOf("year"))
+			.with(AnalyticsTimeRanges.Past7Days, () => dayjsLib().subtract(7, "day"))
+			.with(AnalyticsTimeRanges.Past30Days, () =>
+				dayjsLib().subtract(30, "day"),
+			)
+			.with(AnalyticsTimeRanges.Past6Months, () =>
+				dayjsLib().subtract(6, "month"),
+			)
+			.with(AnalyticsTimeRanges.Past12Months, () =>
+				dayjsLib().subtract(12, "month"),
+			)
+			.with(AnalyticsTimeRanges.AllTime, () =>
+				loaderData.userAnalyticsParameters.startDate
+					? dayjsLib(loaderData.userAnalyticsParameters.startDate)
+					: undefined,
+			)
+			.with(AnalyticsTimeRanges.Custom, () => undefined)
+			.exhaustive();
+
+	const startDate =
+		timeSpanSettings.startDate ||
+		formatDateToNaiveDate(getStartTime(timeSpanSettings.range) || new Date());
+
+	const endDate =
+		timeSpanSettings.endDate ||
+		(timeSpanSettings.range === "All Time" &&
+			loaderData.userAnalyticsParameters.endDate) ||
+		formatDateToNaiveDate(dayjsLib());
+	return { timeSpanSettings, setTimeSpanSettings, startDate, endDate };
+};
+
+const useGetUserAnalytics = () => {
+	const { startDate, endDate } = useTimeSpanSettings();
+	const input = { dateRange: { startDate, endDate } };
+
+	const { data: userAnalytics } = useQuery({
+		queryKey: queryFactory.analytics.user({ input }).queryKey,
+		queryFn: async () => {
+			return await clientGqlService
+				.request(UserAnalyticsDocument, { input })
+				.then((data) => data.userAnalytics);
+		},
+	});
+	return userAnalytics;
+};
+
+const isCaptureLoadingAtom = atom(false);
+
+export default function Page() {
+	const coreDetails = useCoreDetails();
+	const [customRangeOpened, setCustomRangeOpened] = useState(false);
+	const toCaptureRef = useRef<HTMLDivElement>(null);
+	const { timeSpanSettings, setTimeSpanSettings, startDate, endDate } =
+		useTimeSpanSettings();
+	const [isCaptureLoading, setIsCaptureLoading] = useAtom(isCaptureLoadingAtom);
+
+	return (
+		<>
+			<CustomDateSelectModal
+				opened={customRangeOpened}
+				onClose={() => setCustomRangeOpened(false)}
+			/>
+			<Container
+				py="md"
+				ref={toCaptureRef}
+				style={{ backgroundColor: "var(--mantine-color-body)" }}
+			>
+				<Stack>
+					<SimpleGrid cols={{ base: 2 }} style={{ alignItems: "center" }}>
+						<Text fz={{ base: "lg", md: "h1" }} fw="bold">
+							Analytics
+						</Text>
+						<ClientOnly>
+							{() => (
+								<Menu position="bottom-end">
+									<Menu.Target>
+										<Button
+											w={{ md: 200 }}
+											variant="default"
+											ml={{ md: "auto" }}
+										>
+											<Stack gap={0}>
+												<Text size="xs">{timeSpanSettings.range}</Text>
+												<Text span c="dimmed" size="xs">
+													{startDate} to {endDate}
+												</Text>
+											</Stack>
+										</Button>
+									</Menu.Target>
+									<Menu.Dropdown>
+										{Object.values(AnalyticsTimeRanges).map((range) => (
+											<Menu.Item
+												ta="right"
+												key={range}
+												color={
+													timeSpanSettings.range === range ? "blue" : undefined
+												}
+												onClick={() => {
+													if (range === "Custom")
+														return setCustomRangeOpened(true);
+													setTimeSpanSettings(
+														produce(timeSpanSettings, (draft) => {
+															draft.range = range;
+															draft.startDate = undefined;
+															draft.endDate = undefined;
+														}),
+													);
+												}}
+											>
+												{range}
+											</Menu.Item>
+										))}
+									</Menu.Dropdown>
+								</Menu>
+							)}
+						</ClientOnly>
+					</SimpleGrid>
+					{!coreDetails.isServerKeyValidated &&
+					![
+						AnalyticsTimeRanges.Yesterday,
+						AnalyticsTimeRanges.Past7Days,
+						AnalyticsTimeRanges.Past30Days,
+					].includes(timeSpanSettings.range) ? (
+						<ProRequiredAlert />
+					) : (
+						<Grid>
+							<Grid.Col span={{ base: 12, md: 6 }}>
+								<MusclesChart />
+							</Grid.Col>
+							<Grid.Col span={{ base: 12, md: 6 }}>
+								<ExercisesChart />
+							</Grid.Col>
+							<Grid.Col span={{ base: 12, md: 6 }}>
+								<TimeOfDayChart />
+							</Grid.Col>
+							<Grid.Col span={{ base: 12, md: 6 }}>
+								<StatisticsCard />
+							</Grid.Col>
+							<Grid.Col span={12}>
+								<ActivitySection />
+							</Grid.Col>
+						</Grid>
+					)}
+					{isCaptureLoading ? (
+						<Text ta="right" size="xs" c="dimmed">
+							Generated using Ryot (https://ryot.io)
+						</Text>
+					) : null}
+				</Stack>
+			</Container>
+			<Container>
+				<Flex w="100%" mt="md">
+					<Button
+						mr="md"
+						ml="auto"
+						variant="default"
+						loading={isCaptureLoading}
+						leftSection={<IconImageInPicture />}
+						onClick={() => {
+							if (!coreDetails.isServerKeyValidated)
+								return notifications.show({
+									color: "red",
+									title: "Pro required",
+									message: PRO_REQUIRED_MESSAGE,
+								});
+							const current = toCaptureRef.current;
+							if (!current) return;
+							setIsCaptureLoading(true);
+							setTimeout(async () => {
+								try {
+									const canvasPromise = await html2canvas(current);
+									const dataURL = canvasPromise.toDataURL("image/png");
+									const img = new Image();
+									img.setAttribute("src", dataURL);
+									img.setAttribute("download", dataURL);
+									const a = document.createElement("a");
+									a.setAttribute("download", dataURL);
+									a.setAttribute("href", img.src);
+									a.setAttribute("target", "_blank");
+									a.innerHTML = "DOWNLOAD";
+									document.body.appendChild(a);
+									a.click();
+								} catch {
+									notifications.show({
+										color: "red",
+										title: "Error",
+										message: "Something went wrong while capturing the image",
+									});
+								} finally {
+									setIsCaptureLoading(false);
+								}
+							}, 1500);
+						}}
+					>
+						Save image
+					</Button>
+				</Flex>
+			</Container>
+		</>
+	);
+}
+
+const DisplayStat = (props: {
+	label: string;
+	value: string | number;
+}) => {
+	return (
+		<Stack gap={4}>
+			<Text c="dimmed">{props.label}</Text>
+			<Text size="xl" fw="bolder">
+				{props.value}
+			</Text>
+		</Stack>
+	);
+};
+
+const ActivitySection = () => {
+	const userAnalytics = useGetUserAnalytics();
+	const dailyUserActivities = userAnalytics?.activities;
+	const trackSeries = mapValues(MediaColors, () => false);
+
+	const data = dailyUserActivities?.items.map((d) => {
+		const data = Object.entries(d)
+			.filter(([_, value]) => value !== 0)
+			.map(([key, value]) => ({
+				[snakeCase(
+					key.replace("Count", "").replace("total", ""),
+				).toUpperCase()]: value,
+			}))
+			.reduce(Object.assign, {});
+		for (const key in data)
+			if (isBoolean(trackSeries[key])) trackSeries[key] = true;
+		return data;
+	});
+	const series = pickBy(trackSeries);
+	const dailyUserActivitiesData = dailyUserActivities
+		? {
+				data,
+				series,
+				groupedBy: dailyUserActivities.groupedBy,
+				totalCount: dailyUserActivities.totalCount,
+				totalDuration: dailyUserActivities.totalDuration,
+			}
+		: undefined;
+
+	const items = dailyUserActivitiesData?.totalCount || 0;
+
+	return (
+		<Paper p={items === 0 ? "md" : undefined} withBorder={items === 0}>
+			<Stack h={{ base: 500, md: 400 }}>
+				<SimpleGrid cols={{ base: 2, md: 3 }} mx={{ md: "xl" }}>
+					<DisplayStat
+						label="Total"
+						value={`${formatQuantityWithCompactNotation(Number(items))} items`}
+					/>
+					<DisplayStat
+						label="Duration"
+						value={
+							dailyUserActivitiesData
+								? humanizeDuration(
+										dayjsLib
+											.duration(
+												dailyUserActivitiesData.totalDuration,
+												"minutes",
+											)
+											.asMilliseconds(),
+										{ largest: 2 },
+									)
+								: "N/A"
+						}
+					/>
+				</SimpleGrid>
+				{dailyUserActivitiesData ? (
+					dailyUserActivitiesData.totalCount !== 0 &&
+					dailyUserActivitiesData.data ? (
+						<BarChart
+							h="100%"
+							w="100%"
+							ml={-15}
+							withLegend
+							tickLine="x"
+							dataKey="DAY"
+							type="stacked"
+							data={dailyUserActivitiesData.data}
+							legendProps={{ verticalAlign: "bottom" }}
+							series={Object.keys(dailyUserActivitiesData.series).map(
+								(lot) => ({
+									name: lot,
+									color: MediaColors[lot],
+									label: changeCase(lot),
+								}),
+							)}
+							xAxisProps={{
+								tickFormatter: (v) =>
+									dayjsLib(v).format(
+										match(dailyUserActivitiesData.groupedBy)
+											.with(
+												DailyUserActivitiesResponseGroupedBy.Day,
+												() => "MMM D",
+											)
+											.with(
+												DailyUserActivitiesResponseGroupedBy.Month,
+												() => "MMM",
+											)
+											.with(
+												DailyUserActivitiesResponseGroupedBy.Year,
+												DailyUserActivitiesResponseGroupedBy.Millennium,
+												() => "YYYY",
+											)
+											.exhaustive(),
+									),
+							}}
+						/>
+					) : (
+						<Center h="100%">
+							<Text m="auto" ta="center">
+								No activity found in the selected period
+							</Text>
+						</Center>
+					)
+				) : (
+					<Center h="100%">
+						<Loader />
+					</Center>
+				)}
+			</Stack>
+		</Paper>
+	);
+};
+
+const CustomDateSelectModal = (props: {
+	opened: boolean;
+	onClose: () => void;
+}) => {
+	const loaderData = useLoaderData<typeof loader>();
+	const { timeSpanSettings, setTimeSpanSettings, startDate, endDate } =
+		useTimeSpanSettings();
+	const [value, setValue] = useState<[Date | null, Date | null]>([
+		new Date(startDate),
+		new Date(endDate),
+	]);
+
+	return (
+		<Modal
+			opened={props.opened}
+			title="Select custom date range"
+			onClose={props.onClose}
+		>
+			<Stack>
+				<DatePicker
+					mx="auto"
+					size="md"
+					type="range"
+					value={value}
+					w="fit-content"
+					onChange={setValue}
+					minDate={dayjsLib(
+						loaderData.userAnalyticsParameters.startDate,
+					).toDate()}
+					maxDate={dayjsLib(
+						loaderData.userAnalyticsParameters.endDate,
+					).toDate()}
+				/>
+				<Button
+					variant="default"
+					leftSection={<IconDeviceFloppy />}
+					onClick={() => {
+						setTimeSpanSettings(
+							produce(timeSpanSettings, (draft) => {
+								draft.startDate = formatDateToNaiveDate(value[0] || new Date());
+								draft.endDate = formatDateToNaiveDate(value[1] || new Date());
+								draft.range = AnalyticsTimeRanges.Custom;
+							}),
+						);
+						props.onClose();
+					}}
+				>
+					Apply
+				</Button>
+			</Stack>
+		</Modal>
+	);
+};
+
+const MusclesChart = () => {
+	const colors = useGetMantineColors();
+
+	return (
+		<ChartContainer title="Muscles worked out">
+			{(count, { fitness }) => ({
+				totalItems: fitness.workoutMuscles.length,
+				render: (
+					<PieChart
+						size={250}
+						withLabels
+						withTooltip
+						strokeWidth={0.5}
+						labelsType="percent"
+						tooltipDataSource="segment"
+						data={fitness.workoutMuscles.slice(0, count).map((item) => ({
+							value: item.count,
+							name: changeCase(item.muscle),
+							color: selectRandomElement(colors, item.muscle),
+						}))}
+					/>
+				),
+			})}
+		</ChartContainer>
+	);
+};
+
+const ExercisesChart = () => {
+	const colors = useGetMantineColors();
+
+	return (
+		<ChartContainer title="Exercises done">
+			{(count, { fitness }) => ({
+				totalItems: fitness.workoutExercises.length,
+				render: (
+					<BarChart
+						h={300}
+						withTooltip
+						dataKey="name"
+						gridAxis="none"
+						tickLine="none"
+						tooltipAnimationDuration={500}
+						series={[{ name: "value", label: "Times done" }]}
+						data={fitness.workoutExercises.slice(0, count).map((item) => ({
+							value: item.count,
+							name: changeCase(item.exercise),
+							color: selectRandomElement(colors, item.exercise),
+						}))}
+					/>
+				),
+			})}
+		</ChartContainer>
+	);
+};
+
+const TimeOfDayChart = () => {
+	return (
+		<ChartContainer title="Time of day" disableCounter>
+			{(_, data) => {
+				const trackSeries = new Set<string>();
+				const allHours = Array.from({ length: 24 }, (_, h) => h).map((h) => {
+					const obj: Record<string, string | number> = { hour: h };
+					for (const mKey in MediaColors) {
+						const count =
+							data.hours
+								.find((d) => d.hour === h)
+								?.entities.filter(
+									(e) => e.entityLot === mKey || (e.metadataLot || "") === mKey,
+								).length || 0;
+						obj[mKey] = count;
+						if (count > 0) trackSeries.add(mKey);
+					}
+					obj.hour = dayjsLib()
+						.hour(convertUtcHourToLocalHour(h))
+						.format("h a");
+					return obj;
+				});
+				const filteredHours = allHours.filter(
+					(h) =>
+						sum(
+							Object.entries(h)
+								.filter((a) => a[0] !== "hour")
+								.map((f) => f[1]),
+						) > 0,
+				);
+				return {
+					totalItems: filteredHours.length,
+					render: (
+						<RadarChart
+							h={300}
+							w="100%"
+							dataKey="hour"
+							withPolarRadiusAxis
+							data={filteredHours}
+							series={[...trackSeries].map((key) => ({
+								name: key,
+								opacity: 0.3,
+								color: MediaColors[key],
+							}))}
+						/>
+					),
+				};
+			}}
+		</ChartContainer>
+	);
+};
+
+const StatItem = (props: {
+	text: string;
+	label: string;
+	icon: ComponentType<IconProps>;
+}) => {
+	return (
+		<Stack align="center" justify="center" gap={0}>
+			<props.icon size={20} />
+			<Text size="sm" c="dimmed" ta="center">
+				{props.label}
+			</Text>
+			<Text size="xl" fw="bold" ta="center">
+				{props.text}
+			</Text>
+		</Stack>
+	);
+};
+
+const StatisticsCard = () => {
+	const unitSystem = useUserUnitSystem();
+
+	const displayDuration = (duration: number) => {
+		return humanizeDuration(
+			dayjsLib.duration(duration, "minutes").asMilliseconds(),
+			{ largest: 1 },
+		);
+	};
+
+	return (
+		<ChartContainer title="Fitness Statistics" disableCounter>
+			{(_, { fitness }) => ({
+				totalItems: fitness.workoutCount + fitness.measurementCount,
+				render: (
+					<SimpleGrid
+						cols={3}
+						h="100%"
+						w="100%"
+						p={{ md: "xl" }}
+						py={{ base: "md", md: "auto" }}
+					>
+						<StatItem
+							label="Workouts"
+							icon={IconStretching}
+							text={`${fitness.workoutCount}`}
+						/>
+						<StatItem
+							label="Reps"
+							icon={IconRepeat}
+							text={formatQuantityWithCompactNotation(fitness.workoutReps)}
+						/>
+						<StatItem
+							label="Weight"
+							icon={IconWeight}
+							text={displayWeightWithUnit(
+								unitSystem,
+								fitness.workoutWeight,
+								true,
+							)}
+						/>
+						<StatItem
+							icon={IconRoad}
+							label="Distance"
+							text={displayDistanceWithUnit(
+								unitSystem,
+								fitness.workoutDistance,
+							)}
+						/>
+						<StatItem
+							label="Duration"
+							icon={IconClock}
+							text={displayDuration(fitness.workoutDuration)}
+						/>
+						<StatItem
+							label="Exercises"
+							icon={IconBarbell}
+							text={`${fitness.workoutExercises.length}`}
+						/>
+						<StatItem
+							icon={IconTrophy}
+							label="Personal Bests"
+							text={`${fitness.workoutPersonalBests}`}
+						/>
+						<StatItem
+							icon={IconZzz}
+							label="Rest Time"
+							text={displayDuration(fitness.workoutRestTime)}
+						/>
+						<StatItem
+							label="Measurements"
+							icon={IconRulerMeasure}
+							text={`${fitness.measurementCount}`}
+						/>
+					</SimpleGrid>
+				),
+			})}
+		</ChartContainer>
+	);
+};
+
+type ChartContainerProps = {
+	title: string;
+	disableCounter?: boolean;
+	children: (
+		count: number,
+		data: UserAnalyticsQuery["userAnalytics"],
+	) => {
+		render: ReactNode;
+		totalItems: number;
+	};
+};
+
+const ChartContainer = (props: ChartContainerProps) => {
+	const userPreferences = useUserPreferences();
+	const [isCaptureLoading] = useAtom(isCaptureLoadingAtom);
+	const [count, setCount] = useLocalStorage(
+		`FitnessChartContainer-${props.title}`,
+		10,
+	);
+	const userAnalytics = useGetUserAnalytics();
+
+	const value = userAnalytics
+		? props.children(count, userAnalytics)
+		: undefined;
+
+	return userPreferences.featuresEnabled.fitness.enabled ? (
+		<Paper display="flex" h={380} withBorder={value?.totalItems === 0} p="md">
+			<Flex flex={1} align="center" direction="column" w="100%">
+				<Group wrap="nowrap" w="100%" gap="xl" justify="center">
+					<Text size="lg" fw="bold">
+						{props.title}
+					</Text>
+					{props.disableCounter ||
+					(value?.totalItems || 0) === 0 ||
+					isCaptureLoading ? null : (
+						<NumberInput
+							w={60}
+							min={2}
+							size="xs"
+							value={count}
+							max={value?.totalItems}
+							onFocus={(e) => e.target.select()}
+							onChange={(v) => setCount(Number(v))}
+						/>
+					)}
+				</Group>
+				<Flex flex={1} w="100%" justify="center" align="center">
+					{value ? (
+						value.totalItems > 0 ? (
+							value.render
+						) : (
+							<Text fz="lg" mt="xl">
+								No data found
+							</Text>
+						)
+					) : (
+						<Loader />
+					)}
+				</Flex>
+			</Flex>
+		</Paper>
+	) : null;
+};
diff --git a/apps/frontend/app/routes/_dashboard.collections.list.tsx b/apps/frontend/app/routes/_dashboard.collections.list.tsx
index 326c32ed50..d800b7770a 100644
--- a/apps/frontend/app/routes/_dashboard.collections.list.tsx
+++ b/apps/frontend/app/routes/_dashboard.collections.list.tsx
@@ -352,7 +352,7 @@ const DisplayCollection = (props: {
 					pos="relative"
 					style={{ overflow: "hidden" }}
 				>
-					{coreDetails.isPro ? (
+					{coreDetails.isServerKeyValidated ? (
 						collectionImages && collectionImages.length > 0 ? (
 							collectionImages.map((image, index) => {
 								const shouldCollapse = index < currentlyHovered;
@@ -535,11 +535,15 @@ const CreateOrUpdateModal = (props: {
 					label="Description"
 					defaultValue={props.toUpdateCollection?.description}
 				/>
-				<Tooltip label={PRO_REQUIRED_MESSAGE} disabled={coreDetails.isPro}>
+				<Tooltip
+					label={PRO_REQUIRED_MESSAGE}
+					disabled={coreDetails.isServerKeyValidated}
+				>
 					<MultiSelect
+						searchable
 						name="collaborators"
+						disabled={!coreDetails.isServerKeyValidated}
 						description="Add collaborators to this collection"
-						searchable
 						defaultValue={(props.toUpdateCollection?.collaborators || []).map(
 							(c) => c.id,
 						)}
@@ -548,7 +552,6 @@ const CreateOrUpdateModal = (props: {
 							label: u.name,
 							disabled: u.id === userDetails.id,
 						}))}
-						disabled={!coreDetails.isPro}
 					/>
 				</Tooltip>
 				<Input.Wrapper
@@ -559,7 +562,7 @@ const CreateOrUpdateModal = (props: {
 							<Anchor
 								size="xs"
 								onClick={() => {
-									if (!coreDetails.isPro) {
+									if (!coreDetails.isServerKeyValidated) {
 										notifications.show({
 											color: "red",
 											message: PRO_REQUIRED_MESSAGE,
diff --git a/apps/frontend/app/routes/_dashboard.fitness.$action.tsx b/apps/frontend/app/routes/_dashboard.fitness.$action.tsx
index 8d41cfcfa6..0f4a3c4b0d 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.$action.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.$action.tsx
@@ -808,7 +808,7 @@ const CreateSupersetModal = (props: {
 								else setExercisesHandle.append(ex.identifier);
 							}}
 						>
-							{ex.exerciseId}
+							{ex.name}
 						</Button>
 					);
 				})}
@@ -868,7 +868,7 @@ const EditSupersetModal = (props: {
 								else setExercisesHandle.append(ex.identifier);
 							}}
 						>
-							{ex.exerciseId}
+							{ex.name}
 						</Button>
 					);
 				})}
@@ -1016,7 +1016,7 @@ const ExerciseDisplay = (props: {
 				withCloseButton={false}
 			>
 				<Stack>
-					<Text size="lg">Images for {exercise.exerciseId}</Text>
+					<Text size="lg">Images for {exercise.name}</Text>
 					{fileUploadAllowed ? (
 						<>
 							{exercise.images.length > 0 ? (
@@ -1102,7 +1102,7 @@ const ExerciseDisplay = (props: {
 			<Paper
 				radius={0}
 				style={{
-					scrollMargin: "60px",
+					scrollMargin: exercise.scrollMarginRemoved ? "10px" : "60px",
 					borderLeft: partOfSuperset
 						? `3px solid ${theme.colors[partOfSuperset.color][6]}`
 						: undefined,
@@ -1121,7 +1121,7 @@ const ExerciseDisplay = (props: {
 								component={Link}
 								to={getExerciseDetailsPath(exercise.exerciseId)}
 							>
-								{exercise.exerciseId}
+								{exercise.name}
 							</Anchor>
 							<Group wrap="nowrap" mr={-10}>
 								{didExerciseActivateTimer ? (
@@ -1183,7 +1183,7 @@ const ExerciseDisplay = (props: {
 							<Menu.Item
 								leftSection={<IconReplace size={14} />}
 								onClick={() => {
-									if (!coreDetails.isPro) {
+									if (!coreDetails.isServerKeyValidated) {
 										notifications.show({
 											message: PRO_REQUIRED_MESSAGE,
 											color: "red",
@@ -1229,7 +1229,7 @@ const ExerciseDisplay = (props: {
 								leftSection={<IconTrash size={14} />}
 								onClick={async () => {
 									const yes = await confirmWrapper({
-										confirmation: `This removes '${exercise.exerciseId}' and all its sets from your workout. You can not undo this action. Are you sure you want to continue?`,
+										confirmation: `This removes '${exercise.name}' and all its sets from your workout. You can not undo this action. Are you sure you want to continue?`,
 									});
 									if (yes) {
 										const assets = [...exercise.images, ...exercise.videos];
@@ -1311,7 +1311,7 @@ const ExerciseDisplay = (props: {
 																entityId={history.workoutId}
 																entityType={FitnessEntity.Workouts}
 																onCopyButtonClick={async () => {
-																	if (!coreDetails.isPro) {
+																	if (!coreDetails.isServerKeyValidated) {
 																		notifications.show({
 																			color: "red",
 																			message:
@@ -1359,7 +1359,7 @@ const ExerciseDisplay = (props: {
 											pos="absolute"
 											p={2}
 											onClick={() => {
-												if (!coreDetails.isPro) {
+												if (!coreDetails.isServerKeyValidated) {
 													notifications.show({
 														color: "red",
 														message: PRO_REQUIRED_MESSAGE,
@@ -1659,7 +1659,7 @@ const SetDisplay = (props: {
 							fz="xs"
 							leftSection={<IconClipboard size={14} />}
 							onClick={() => {
-								if (!coreDetails.isPro) {
+								if (!coreDetails.isServerKeyValidated) {
 									notifications.show({
 										color: "red",
 										message: PRO_REQUIRED_MESSAGE,
@@ -1855,6 +1855,7 @@ const SetDisplay = (props: {
 												draft.exercises[props.exerciseIdx];
 											currentExercise.sets[props.setIdx].confirmedAt =
 												newConfirmed ? dayjsLib().toISOString() : null;
+											currentExercise.scrollMarginRemoved = true;
 											if (newConfirmed) {
 												const nextSet = getNextSetInWorkout(
 													draft,
@@ -2241,7 +2242,7 @@ const ReorderDrawer = (props: { opened: boolean; onClose: () => void }) => {
 											{...provided.dragHandleProps}
 										>
 											<Group justify="space-between" wrap="nowrap">
-												<Text size="sm">{de.exerciseId}</Text>
+												<Text size="sm">{de.name}</Text>
 												<ThemeIcon size="xs" variant="transparent" color="gray">
 													{match(getProgressOfExercise(currentWorkout, index))
 														.with("complete", () => <IconDropletFilled />)
diff --git a/apps/frontend/app/routes/_dashboard.fitness.$entity.$id._index.tsx b/apps/frontend/app/routes/_dashboard.fitness.$entity.$id._index.tsx
index 44409e5840..b1c3d04f10 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.$entity.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.$entity.$id._index.tsx
@@ -368,7 +368,7 @@ export default function Page() {
 											</Menu.Item>
 											<Menu.Item
 												onClick={() => {
-													if (!coreDetails.isPro) {
+													if (!coreDetails.isServerKeyValidated) {
 														notifications.show({
 															color: "red",
 															message: PRO_REQUIRED_MESSAGE,
@@ -560,9 +560,9 @@ export default function Page() {
 					{loaderData.information.exercises.map((exercise, idx) => (
 						<ExerciseHistory
 							exerciseIdx={idx}
+							key={`${exercise.id}-${idx}`}
 							entityId={loaderData.entityId}
 							entityType={loaderData.entity}
-							key={`${exercise.name}-${idx}`}
 							supersetInformation={loaderData.information.supersets}
 						/>
 					))}
diff --git a/apps/frontend/app/routes/_dashboard.fitness.$entity.list.tsx b/apps/frontend/app/routes/_dashboard.fitness.$entity.list.tsx
index 8eca379250..2b9af7bdfe 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.$entity.list.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.$entity.list.tsx
@@ -10,6 +10,7 @@ import {
 	Flex,
 	Group,
 	Pagination,
+	Skeleton,
 	Stack,
 	Text,
 	Title,
@@ -63,7 +64,10 @@ import {
 	useGetWorkoutStarter,
 	useUserUnitSystem,
 } from "~/lib/hooks";
-import { getDefaultWorkout } from "~/lib/state/fitness";
+import {
+	getDefaultWorkout,
+	getExerciseDetailsQuery,
+} from "~/lib/state/fitness";
 import {
 	getEnhancedCookieName,
 	redirectToFirstPageIfOnInvalidPage,
@@ -93,20 +97,8 @@ export const loader = async ({ params, request }: LoaderFunctionArgs) => {
 				{ input: { page: query[pageQueryParam], query: query.query } },
 			);
 			return {
+				items: userWorkoutsList.items,
 				details: userWorkoutsList.details,
-				items: userWorkoutsList.items.map((w) => ({
-					id: w.id,
-					name: w.name,
-					timestamp: w.startTime,
-					detail: humanizeDuration(
-						dayjsLib.duration(w.duration, "second").asMilliseconds(),
-						{
-							round: true,
-							units: ["h", "m"],
-						},
-					),
-					summary: w.summary,
-				})),
 			};
 		})
 		.with(FitnessEntity.Templates, async () => {
@@ -117,14 +109,8 @@ export const loader = async ({ params, request }: LoaderFunctionArgs) => {
 					{ input: { page: query.page, query: query.query } },
 				);
 			return {
+				items: userWorkoutTemplatesList.items,
 				details: userWorkoutTemplatesList.details,
-				items: userWorkoutTemplatesList.items.map((w) => ({
-					id: w.id,
-					name: w.name,
-					timestamp: w.createdOn,
-					detail: changeCase(w.visibility),
-					summary: w.summary,
-				})),
 			};
 		})
 		.exhaustive();
@@ -156,7 +142,7 @@ export default function Page() {
 						variant="outline"
 						onClick={() => {
 							if (
-								!coreDetails.isPro &&
+								!coreDetails.isServerKeyValidated &&
 								loaderData.entity === FitnessEntity.Templates
 							) {
 								notifications.show({
@@ -185,11 +171,11 @@ export default function Page() {
 				/>
 				{loaderData.itemList.items.length > 0 ? (
 					<Stack gap="xs">
-						{loaderData.itemList.items.map((workout, index) => (
-							<DisplayWorkoutListItem
+						{loaderData.itemList.items.map((entityId, index) => (
+							<DisplayFitnessEntity
 								index={index}
-								data={workout}
-								key={workout.id}
+								key={entityId}
+								entityId={entityId}
 							/>
 						))}
 					</Stack>
@@ -209,53 +195,69 @@ export default function Page() {
 	);
 }
 
-type DataItem = Awaited<ReturnType<typeof loader>>["itemList"]["items"][number];
-
-const DisplayWorkoutListItem = ({
-	data,
-	index,
-}: { data: DataItem; index: number }) => {
+const DisplayFitnessEntity = (props: { entityId: string; index: number }) => {
 	const loaderData = useLoaderData<typeof loader>();
 	const unitSystem = useUserUnitSystem();
 	const [parent] = useAutoAnimate();
 	const [showDetails, setShowDetails] = useDisclosure(false);
 
 	const { data: entityInformation } = useQuery({
-		queryKey: ["fitnessEntityDetails", data.id],
+		queryKey: ["fitnessEntityDetails", props.entityId],
 		queryFn: () =>
 			match(loaderData.entity)
 				.with(FitnessEntity.Workouts, () =>
 					clientGqlService
-						.request(UserWorkoutDetailsDocument, { workoutId: data.id })
-						.then((data) => data.userWorkoutDetails.details.information),
+						.request(UserWorkoutDetailsDocument, { workoutId: props.entityId })
+						.then(({ userWorkoutDetails }) => ({
+							name: userWorkoutDetails.details.name,
+							summary: userWorkoutDetails.details.summary,
+							timestamp: userWorkoutDetails.details.startTime,
+							information: userWorkoutDetails.details.information,
+							detail: humanizeDuration(
+								dayjsLib
+									.duration(userWorkoutDetails.details.duration, "second")
+									.asMilliseconds(),
+								{ round: true, units: ["h", "m"] },
+							),
+						})),
 				)
 				.with(FitnessEntity.Templates, () =>
 					clientGqlService
 						.request(UserWorkoutTemplateDetailsDocument, {
-							workoutTemplateId: data.id,
+							workoutTemplateId: props.entityId,
 						})
-						.then(
-							(data) => data.userWorkoutTemplateDetails.details.information,
-						),
+						.then(({ userWorkoutTemplateDetails }) => ({
+							name: userWorkoutTemplateDetails.details.name,
+							summary: userWorkoutTemplateDetails.details.summary,
+							timestamp: userWorkoutTemplateDetails.details.createdOn,
+							information: userWorkoutTemplateDetails.details.information,
+							detail: changeCase(userWorkoutTemplateDetails.details.visibility),
+						})),
 				)
 				.exhaustive(),
 	});
 
-	const personalBestsAchieved = data.summary.total?.personalBestsAchieved || 0;
-	const repsData = (entityInformation?.exercises || [])
+	if (!entityInformation)
+		return (
+			<Stack gap={4}>
+				<Skeleton height={76} />
+				<Group wrap="nowrap" justify="space-between" gap={4}>
+					<Skeleton height={40} />
+					<Skeleton height={40} />
+				</Group>
+			</Stack>
+		);
+
+	const personalBestsAchieved =
+		entityInformation.summary.total?.personalBestsAchieved || 0;
+	const repsData = (entityInformation.information.exercises || [])
 		.map((e) => Number.parseInt(e.total?.reps || "0"))
 		.filter(Boolean);
 
 	return (
 		<>
-			{index !== 0 ? <Divider /> : null}
-			<Stack
-				gap="xs"
-				ref={parent}
-				key={data.id}
-				data-workout-id={data.id}
-				px={{ base: "xs", md: "md" }}
-			>
+			{props.index !== 0 ? <Divider /> : null}
+			<Stack gap="xs" ref={parent} px={{ base: "xs", md: "md" }}>
 				<Group wrap="nowrap" justify="space-between">
 					<Box>
 						<Group wrap="nowrap">
@@ -263,27 +265,25 @@ const DisplayWorkoutListItem = ({
 								component={Link}
 								fz={{ base: "sm", md: "md" }}
 								to={$path("/fitness/:entity/:id", {
+									id: props.entityId,
 									entity: loaderData.entity,
-									id: data.id,
 								})}
 							>
-								{truncate(data.name, { length: 20 })}
+								{truncate(entityInformation.name, { length: 20 })}
 							</Anchor>
 							<Text fz={{ base: "xs", md: "sm" }} c="dimmed">
-								{dayjsLib(data.timestamp).format("LL")}
+								{dayjsLib(entityInformation.timestamp).format("LL")}
 							</Text>
 						</Group>
 						<Group mt="xs">
-							{data.detail ? (
-								<DisplayStat
-									icon={match(loaderData.entity)
-										.with(FitnessEntity.Workouts, () => <IconClock size={16} />)
-										.with(FitnessEntity.Templates, () => <IconLock size={16} />)
-										.exhaustive()}
-									data={data.detail}
-								/>
-							) : null}
-							{data.summary.total ? (
+							<DisplayStat
+								data={entityInformation.detail}
+								icon={match(loaderData.entity)
+									.with(FitnessEntity.Workouts, () => <IconClock size={16} />)
+									.with(FitnessEntity.Templates, () => <IconLock size={16} />)
+									.exhaustive()}
+							/>
+							{entityInformation.summary.total ? (
 								<>
 									{personalBestsAchieved !== 0 ? (
 										<DisplayStat
@@ -293,22 +293,22 @@ const DisplayWorkoutListItem = ({
 											}`}
 										/>
 									) : null}
-									{Number(data.summary.total.weight) !== 0 ? (
+									{Number(entityInformation.summary.total.weight) !== 0 ? (
 										<DisplayStat
 											icon={<IconWeight size={16} />}
 											data={displayWeightWithUnit(
 												unitSystem,
-												data.summary.total.weight,
+												entityInformation.summary.total.weight,
 											)}
 										/>
 									) : null}
-									{Number(data.summary.total.distance) !== 0 ? (
+									{Number(entityInformation.summary.total.distance) !== 0 ? (
 										<Box visibleFrom="md">
 											<DisplayStat
 												icon={<IconRoad size={16} />}
 												data={displayDistanceWithUnit(
 													unitSystem,
-													data.summary.total.distance,
+													entityInformation.summary.total.distance,
 												)}
 											/>
 										</Box>
@@ -336,10 +336,10 @@ const DisplayWorkoutListItem = ({
 								<Text fw="bold">Best set</Text>
 							) : null}
 						</Group>
-						{data.summary.exercises.map((exercise, idx) => (
+						{entityInformation.summary.exercises.map((exercise, idx) => (
 							<ExerciseDisplay
 								exercise={exercise}
-								key={`${idx}-${exercise.name}`}
+								key={`${idx}-${exercise.id}`}
 							/>
 						))}
 					</Box>
@@ -364,6 +364,9 @@ const ExerciseDisplay = (props: {
 	exercise: WorkoutSummary["exercises"][number];
 }) => {
 	const unitSystem = useUserUnitSystem();
+	const { data: exerciseDetails } = useQuery(
+		getExerciseDetailsQuery(props.exercise.id),
+	);
 	const stat = match(props.exercise.bestSet)
 		.with(undefined, null, () => {})
 		.otherwise((value) => {
@@ -382,7 +385,7 @@ const ExerciseDisplay = (props: {
 				{props.exercise.numSets} ×
 			</Text>
 			<Text style={{ flex: 1 }} fz="sm">
-				{props.exercise.name}
+				{exerciseDetails?.name}
 			</Text>
 			{stat ? <Text fz="sm">{stat}</Text> : null}
 		</Flex>
diff --git a/apps/frontend/app/routes/_dashboard.fitness.exercises.$action.tsx b/apps/frontend/app/routes/_dashboard.fitness.exercises.$action.tsx
index cc6a47f650..86970b4b0d 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.exercises.$action.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.exercises.$action.tsx
@@ -119,11 +119,11 @@ export const action = async ({ request }: ActionFunctionArgs) => {
 				return redirect(getExerciseDetailsPath(createCustomExercise));
 			})
 			.with(Action.Update, async () => {
-				invariant(submission.oldName);
+				invariant(submission.oldId);
 				await serverGqlService.authenticatedRequest(
 					request,
 					UpdateCustomExerciseDocument,
-					{ input: { ...input, oldName: submission.oldName } },
+					{ input: { ...input, oldId: submission.oldId } },
 				);
 				return redirect($path("/fitness/exercises/list"));
 			})
@@ -147,8 +147,8 @@ const optionalString = z.string().optional();
 const optionalStringArray = z.array(z.string()).optional();
 
 const schema = z.object({
-	oldName: optionalString,
-	id: z.string(),
+	oldId: optionalString,
+	name: z.string(),
 	lot: z.nativeEnum(ExerciseLot),
 	level: z.nativeEnum(ExerciseLevel),
 	force: z.nativeEnum(ExerciseForce).optional(),
@@ -179,7 +179,7 @@ export default function Page() {
 					{loaderData.details?.id ? (
 						<input
 							type="hidden"
-							name="oldName"
+							name="oldId"
 							defaultValue={loaderData.details.id}
 						/>
 					) : null}
@@ -187,8 +187,8 @@ export default function Page() {
 						label="Name"
 						required
 						autoFocus
-						name="id"
-						defaultValue={loaderData.details?.id}
+						name="name"
+						defaultValue={loaderData.details?.name}
 					/>
 					<Select
 						label="Type"
diff --git a/apps/frontend/app/routes/_dashboard.fitness.exercises.item.$id._index.tsx b/apps/frontend/app/routes/_dashboard.fitness.exercises.item.$id._index.tsx
index 4c07a78fbc..10688affbc 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.exercises.item.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.exercises.item.$id._index.tsx
@@ -18,6 +18,7 @@ import {
 	Select,
 	SimpleGrid,
 	Stack,
+	Switch,
 	Tabs,
 	Text,
 	Title,
@@ -120,7 +121,7 @@ export const loader = async ({ params, request }: LoaderFunctionArgs) => {
 };
 
 export const meta = ({ data }: MetaArgs<typeof loader>) => {
-	return [{ title: `${data?.exerciseDetails.id} | Ryot` }];
+	return [{ title: `${data?.exerciseDetails.name} | Ryot` }];
 };
 
 export const action = async ({ params, request }: ActionFunctionArgs) => {
@@ -128,11 +129,10 @@ export const action = async ({ params, request }: ActionFunctionArgs) => {
 	const entries = Object.entries(Object.fromEntries(await request.formData()));
 	const submission = [];
 	for (const [property, value] of entries) {
-		if (property.includes("."))
-			submission.push({
-				property,
-				value: value.toString(),
-			});
+		submission.push({
+			property,
+			value: value.toString(),
+		});
 	}
 	for (const change of submission) {
 		await serverGqlService.authenticatedRequest(
@@ -209,7 +209,19 @@ export default function Page() {
 								value={pref[1]}
 							/>
 						))}
-						<Title order={3}>Rest timers</Title>
+						<Switch
+							label="Exclude from analytics"
+							defaultChecked={
+								loaderData.userExerciseDetails.details?.exerciseExtraInformation
+									?.settings.excludeFromAnalytics
+							}
+							onChange={(ev) => {
+								appendPref(
+									"exclude_from_analytics",
+									String(ev.currentTarget.checked),
+								);
+							}}
+						/>
 						<Text size="sm">
 							When a new set is added, rest timers will be added automatically
 							according to the settings below.
@@ -249,7 +261,7 @@ export default function Page() {
 			</Modal>
 			<Container size="xs" px="lg">
 				<Stack>
-					<Title id="exercise-title">{loaderData.exerciseDetails.id}</Title>
+					<Title id="exercise-title">{loaderData.exerciseDetails.name}</Title>
 					{loaderData.userExerciseDetails.collections.length > 0 ? (
 						<Group id="entity-collections">
 							{loaderData.userExerciseDetails.collections.map((col) => (
@@ -582,7 +594,7 @@ export default function Page() {
 											setEntityToReview({
 												entityId: loaderData.exerciseId,
 												entityLot: EntityLot.Exercise,
-												entityTitle: loaderData.exerciseDetails.id,
+												entityTitle: loaderData.exerciseDetails.name,
 											});
 										}}
 									>
diff --git a/apps/frontend/app/routes/_dashboard.fitness.exercises.list.tsx b/apps/frontend/app/routes/_dashboard.fitness.exercises.list.tsx
index 646786d2b4..51c42f9608 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.exercises.list.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.exercises.list.tsx
@@ -367,7 +367,7 @@ export default function Page() {
 												}}
 											>
 												<Flex direction="column" justify="space-around">
-													<Text>{exercise.id}</Text>
+													<Text>{exercise.name}</Text>
 													<Flex>
 														{exercise.muscle ? (
 															<Text size="xs">
diff --git a/apps/frontend/app/routes/_dashboard.fitness.measurements.list.tsx b/apps/frontend/app/routes/_dashboard.fitness.measurements.list.tsx
index 61b2360f7d..defa931fb5 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.measurements.list.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.measurements.list.tsx
@@ -69,15 +69,15 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
 	await redirectUsingEnhancedCookieSearchParams(request, cookieName);
 	const query = zx.parseQuery(request, searchParamsSchema);
 	const now = dayjsLib();
-	const endTime = getDateFromTimeSpan(query.timeSpan || defaultTimeSpan);
+	const startTime = getDateFromTimeSpan(query.timeSpan || defaultTimeSpan);
 	const [{ userMeasurementsList }] = await Promise.all([
 		serverGqlService.authenticatedRequest(
 			request,
 			UserMeasurementsListDocument,
 			{
 				input: {
-					startTime: now.toISOString(),
-					endTime: endTime?.toISOString(),
+					endTime: now.toISOString(),
+					startTime: startTime?.toISOString(),
 				},
 			},
 		),
diff --git a/apps/frontend/app/routes/_dashboard.media.genre.list.tsx b/apps/frontend/app/routes/_dashboard.media.genre.list.tsx
index 6f76c6d722..0dffd685b1 100644
--- a/apps/frontend/app/routes/_dashboard.media.genre.list.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.genre.list.tsx
@@ -159,7 +159,7 @@ const DisplayGenre = (props: { genre: Genre }) => {
 		>
 			<Stack gap={4}>
 				<Box pos="relative">
-					{coreDetails.isPro ? (
+					{coreDetails.isServerKeyValidated ? (
 						<Paper radius="md" style={{ overflow: "hidden" }}>
 							<Flex h={260} w={168} wrap="wrap">
 								{genreImages?.map((image) => (
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 3a4dadbe00..71061f0612 100644
--- a/apps/frontend/app/routes/_dashboard.media.item.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.item.$id._index.tsx
@@ -266,6 +266,7 @@ export default function Page() {
 	const [_m, setMetadataToUpdate] = useMetadataProgressUpdate();
 	const [_r, setEntityToReview] = useReviewEntity();
 	const [_a, setAddEntityToCollectionData] = useAddEntityToCollection();
+	const inProgress = loaderData.userMetadataDetails.inProgress;
 	const nextEntry = loaderData.userMetadataDetails.nextEntry;
 
 	const onSubmitProgressUpdate = (e: React.FormEvent<HTMLFormElement>) => {
@@ -508,14 +509,17 @@ export default function Page() {
 							: null}
 					</Group>
 				) : null}
-				{loaderData.userMetadataDetails?.inProgress ? (
+				{inProgress ? (
 					<Alert icon={<IconAlertCircle />} variant="outline">
 						You are currently{" "}
 						{getVerb(Verb.Read, loaderData.metadataDetails.lot)}
-						ing this (
-						{Number(loaderData.userMetadataDetails.inProgress.progress).toFixed(
-							2,
-						)}
+						ing{" "}
+						{inProgress.podcastExtraInformation
+							? `EP-${inProgress.podcastExtraInformation.episode}`
+							: inProgress.showExtraInformation
+								? `S${inProgress.showExtraInformation.season}-E${inProgress.showExtraInformation.episode}`
+								: "this"}{" "}
+						({Number(inProgress.progress).toFixed(2)}
 						%)
 					</Alert>
 				) : null}
@@ -1339,13 +1343,16 @@ const EditHistoryItemModal = (props: {
 						defaultValue={providerWatchedOn}
 						nothingFoundMessage="No watch providers configured. Please add them in your general preferences."
 					/>
-					<Tooltip label={PRO_REQUIRED_MESSAGE} disabled={coreDetails.isPro}>
+					<Tooltip
+						label={PRO_REQUIRED_MESSAGE}
+						disabled={coreDetails.isServerKeyValidated}
+					>
 						<Select
 							clearable
 							searchable
 							limit={5}
 							name="reviewId"
-							disabled={!coreDetails.isPro}
+							disabled={!coreDetails.isServerKeyValidated}
 							label="Associate with a review"
 							defaultValue={props.seen.reviewId}
 							data={reviewsByThisCurrentUser.map((r) => ({
@@ -1366,14 +1373,17 @@ const EditHistoryItemModal = (props: {
 						label="Time spent"
 						description="How much time did you actually spend on this media?"
 					>
-						<Tooltip label={PRO_REQUIRED_MESSAGE} disabled={coreDetails.isPro}>
+						<Tooltip
+							label={PRO_REQUIRED_MESSAGE}
+							disabled={coreDetails.isServerKeyValidated}
+						>
 							<Group wrap="nowrap" mt="xs">
 								{POSSIBLE_DURATION_UNITS.map((input) => (
 									<NumberInput
 										key={input}
 										rightSectionWidth={36}
-										disabled={!coreDetails.isPro}
 										defaultValue={manualTimeSpentValue[input]}
+										disabled={!coreDetails.isServerKeyValidated}
 										rightSection={<Text size="xs">{input}</Text>}
 										onChange={(v) => {
 											setManualTimeSpentValue((prev) => ({
@@ -1463,7 +1473,7 @@ const HistoryItem = (props: {
 		tab: string,
 		index?: number,
 	) => {
-		if (!coreDetails.isPro) {
+		if (!coreDetails.isServerKeyValidated) {
 			notifications.show({
 				color: "red",
 				message: PRO_REQUIRED_MESSAGE,
diff --git a/apps/frontend/app/routes/_dashboard.settings.integrations.tsx b/apps/frontend/app/routes/_dashboard.settings.integrations.tsx
index ea6c998d8c..892f7c9df1 100644
--- a/apps/frontend/app/routes/_dashboard.settings.integrations.tsx
+++ b/apps/frontend/app/routes/_dashboard.settings.integrations.tsx
@@ -69,6 +69,7 @@ const PRO_INTEGRATIONS = [IntegrationProvider.JellyfinPush];
 const YANK_INTEGRATIONS = [
 	IntegrationProvider.Audiobookshelf,
 	IntegrationProvider.Komga,
+	IntegrationProvider.PlexYank,
 ];
 const PUSH_INTEGRATIONS = [
 	IntegrationProvider.Radarr,
@@ -170,7 +171,9 @@ const createSchema = z.object({
 	syncToOwnedCollection: zx.CheckboxAsString.optional(),
 	providerSpecifics: z
 		.object({
-			plexUsername: z.string().optional(),
+			plexYankBaseUrl: z.string().optional(),
+			plexYankToken: z.string().optional(),
+			plexSinkUsername: z.string().optional(),
 			audiobookshelfBaseUrl: z.string().optional(),
 			audiobookshelfToken: z.string().optional(),
 			komgaBaseUrl: z.string().optional(),
@@ -403,7 +406,9 @@ const CreateIntegrationModal = (props: {
 	const coreDetails = useCoreDetails();
 	const [provider, setProvider] = useState<IntegrationProvider>();
 	const disableCreationButton =
-		!coreDetails.isPro && provider && PRO_INTEGRATIONS.includes(provider);
+		!coreDetails.isServerKeyValidated &&
+		provider &&
+		PRO_INTEGRATIONS.includes(provider);
 
 	return (
 		<Modal
@@ -496,11 +501,25 @@ const CreateIntegrationModal = (props: {
 								/>
 							</>
 						))
-						.with(IntegrationProvider.Plex, () => (
+						.with(IntegrationProvider.PlexYank, () => (
+							<>
+								<TextInput
+									required
+									label="Base URL"
+									name="providerSpecifics.plexYankBaseUrl"
+								/>
+								<TextInput
+									required
+									label="Plex token"
+									name="providerSpecifics.plexYankToken"
+								/>
+							</>
+						))
+						.with(IntegrationProvider.PlexSink, () => (
 							<>
 								<TextInput
 									label="Username"
-									name="providerSpecifics.plexUsername"
+									name="providerSpecifics.plexSinkUsername"
 								/>
 							</>
 						))
@@ -529,14 +548,14 @@ const CreateIntegrationModal = (props: {
 					{provider && YANK_INTEGRATIONS.includes(provider) ? (
 						<Tooltip
 							label="Only available for Pro users"
-							disabled={coreDetails.isPro}
+							disabled={coreDetails.isServerKeyValidated}
 						>
 							<Checkbox
-								label="Sync to Owned collection"
 								name="syncToOwnedCollection"
-								description={`Checking this will also sync items in your library to the "Owned" collection`}
+								label="Sync to Owned collection"
+								disabled={!coreDetails.isServerKeyValidated}
 								styles={{ body: { display: "flex", alignItems: "center" } }}
-								disabled={!coreDetails.isPro}
+								description={`Checking this will also sync items in your library to the "Owned" collection`}
 							/>
 						</Tooltip>
 					) : undefined}
diff --git a/apps/frontend/app/routes/_dashboard.settings.miscellaneous.tsx b/apps/frontend/app/routes/_dashboard.settings.miscellaneous.tsx
index ec6b124237..9b5de45f5c 100644
--- a/apps/frontend/app/routes/_dashboard.settings.miscellaneous.tsx
+++ b/apps/frontend/app/routes/_dashboard.settings.miscellaneous.tsx
@@ -151,9 +151,9 @@ export default function Page() {
 						</Stack>
 						<Stack>
 							<Box>
-								<Title order={4}>Re-evaluate workouts</Title>
+								<Title order={4}>Revise workouts</Title>
 								<Text>
-									Re-evaluate all workouts. This may be useful if exercises done
+									Revise all workouts. This may be useful if exercises done
 									during a workout have changed or workouts have been edited or
 									deleted.
 								</Text>
@@ -161,9 +161,9 @@ export default function Page() {
 							<Button
 								disabled={isEditDisabled}
 								{...btnProps}
-								value={BackgroundJob.ReEvaluateUserWorkouts}
+								value={BackgroundJob.ReviseUserWorkouts}
 							>
-								Re-evaluate workouts
+								Revise workouts
 							</Button>
 						</Stack>
 						<Stack>
diff --git a/apps/frontend/app/routes/_dashboard.settings.preferences.tsx b/apps/frontend/app/routes/_dashboard.settings.preferences.tsx
index da2a446783..fc2551581e 100644
--- a/apps/frontend/app/routes/_dashboard.settings.preferences.tsx
+++ b/apps/frontend/app/routes/_dashboard.settings.preferences.tsx
@@ -270,12 +270,14 @@ export default function Page() {
 					<Tabs.Panel value="features">
 						<Stack>
 							<Text>Features that you want to use.</Text>
-							{(["media", "fitness", "others"] as const).map((facet) => (
-								<Fragment key={facet}>
-									<Title order={4}>{startCase(facet)}</Title>
-									<SimpleGrid cols={2}>
-										{Object.entries(userPreferences.featuresEnabled[facet]).map(
-											([name, isEnabled]) => (
+							{(["media", "fitness", "analytics", "others"] as const).map(
+								(facet) => (
+									<Fragment key={facet}>
+										<Title order={4}>{startCase(facet)}</Title>
+										<SimpleGrid cols={2}>
+											{Object.entries(
+												userPreferences.featuresEnabled[facet],
+											).map(([name, isEnabled]) => (
 												<Switch
 													key={name}
 													size="xs"
@@ -290,11 +292,11 @@ export default function Page() {
 														);
 													}}
 												/>
-											),
-										)}
-									</SimpleGrid>
-								</Fragment>
-							))}
+											))}
+										</SimpleGrid>
+									</Fragment>
+								),
+							)}
 						</Stack>
 					</Tabs.Panel>
 					<Tabs.Panel value="general">
diff --git a/apps/frontend/app/routes/_dashboard.settings.profile-and-sharing.tsx b/apps/frontend/app/routes/_dashboard.settings.profile-and-sharing.tsx
index 8648e6818d..881b807a1c 100644
--- a/apps/frontend/app/routes/_dashboard.settings.profile-and-sharing.tsx
+++ b/apps/frontend/app/routes/_dashboard.settings.profile-and-sharing.tsx
@@ -37,6 +37,7 @@ import {
 	type UserAccessLinksQuery,
 } from "@ryot/generated/graphql/backend/graphql";
 import {
+	formatQuantityWithCompactNotation,
 	getActionIntent,
 	isNumber,
 	isString,
@@ -391,9 +392,7 @@ const DisplayAccessLink = (props: {
 						<Text size="sm">
 							Created: {dayjsLib(props.accessLink.createdOn).fromNow()}, Times
 							Used:{" "}
-							{new Intl.NumberFormat("en-US", {
-								notation: "compact",
-							}).format(props.accessLink.timesUsed)}
+							{formatQuantityWithCompactNotation(props.accessLink.timesUsed)}
 						</Text>
 						{optionalDetails ? <Text size="xs">{optionalDetails}</Text> : null}
 					</Box>
diff --git a/apps/frontend/app/routes/_dashboard.tsx b/apps/frontend/app/routes/_dashboard.tsx
index a8e74c1df1..4985db0f91 100644
--- a/apps/frontend/app/routes/_dashboard.tsx
+++ b/apps/frontend/app/routes/_dashboard.tsx
@@ -91,6 +91,7 @@ import {
 	IconDeviceSpeaker,
 	IconDeviceTv,
 	IconEyeglass,
+	IconGraph,
 	IconHome2,
 	IconLogout,
 	IconMoodEmpty,
@@ -218,7 +219,7 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
 
 	const fitnessLinks = [
 		...(Object.entries(userPreferences.featuresEnabled.fitness || {})
-			.filter(([v, _]) => v !== "enabled")
+			.filter(([v, _]) => !["enabled"].includes(v))
 			.map(([name, enabled]) => ({ name, enabled }))
 			?.filter((f) => f.enabled)
 			.map((f) => ({
@@ -226,10 +227,9 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
 				href: joinURL("/fitness", f.name, "list"),
 			})) || []),
 		{ label: "Exercises", href: $path("/fitness/exercises/list") },
-	].map((link) => ({
-		label: link.label,
-		link: link.href,
-	}));
+	]
+		.filter((link) => link !== undefined)
+		.map((link) => ({ label: link.label, link: link.href }));
 
 	const settingsLinks = [
 		{ label: "Preferences", link: $path("/settings/preferences") },
@@ -652,6 +652,16 @@ export default function Layout() {
 								links={loaderData.fitnessLinks}
 							/>
 						) : null}
+						{loaderData.userPreferences.featuresEnabled.analytics.enabled ? (
+							<LinksGroup
+								opened={false}
+								icon={IconGraph}
+								label="Analytics"
+								setOpened={() => {}}
+								toggle={toggleMobileNavbar}
+								href={$path("/analytics")}
+							/>
+						) : null}
 						{loaderData.userPreferences.featuresEnabled.others.calendar ? (
 							<LinksGroup
 								label="Calendar"
@@ -898,7 +908,7 @@ const Footer = () => {
 	return (
 		<Stack>
 			<Flex gap={80} justify="center">
-				{!coreDetails.isPro ? (
+				{!coreDetails.isServerKeyValidated ? (
 					<Anchor href={coreDetails.websiteUrl} target="_blank">
 						<Text c="red" fw="bold">
 							Ryot Pro
@@ -923,11 +933,12 @@ const Footer = () => {
 	);
 };
 
-const WATCH_TIMES = [
-	"Just Right Now",
-	"I don't remember",
-	"Custom Date",
-] as const;
+enum WatchTimes {
+	JustCompletedNow = "Just Completed Now",
+	IDontRemember = "I don't remember",
+	CustomDate = "Custom Date",
+	JustStartedIt = "Just Started It",
+}
 
 const MetadataProgressUpdateForm = ({
 	closeMetadataProgressUpdateModal,
@@ -968,7 +979,7 @@ const MetadataProgressUpdateForm = ({
 			inProgress={userMetadataDetails.inProgress}
 		/>
 	) : (
-		<NewProgressUpdateForm
+		<MetadataNewProgressUpdateForm
 			onSubmit={onSubmit}
 			metadataDetails={metadataDetails}
 			metadataToUpdate={metadataToUpdate}
@@ -1094,7 +1105,7 @@ const MetadataInProgressUpdateForm = ({
 	);
 };
 
-const NewProgressUpdateForm = ({
+const MetadataNewProgressUpdateForm = ({
 	history,
 	onSubmit,
 	metadataDetails,
@@ -1105,12 +1116,14 @@ const NewProgressUpdateForm = ({
 	metadataDetails: MetadataDetailsQuery["metadataDetails"];
 	history: History;
 }) => {
+	const [parent] = useAutoAnimate();
 	const [_, setMetadataToUpdate] = useMetadataProgressUpdate();
 	const [selectedDate, setSelectedDate] = useState<Date | null | undefined>(
 		new Date(),
 	);
-	const [watchTime, setWatchTime] =
-		useState<(typeof WATCH_TIMES)[number]>("Just Right Now");
+	const [watchTime, setWatchTime] = useState<WatchTimes>(
+		WatchTimes.JustCompletedNow,
+	);
 	const lastProviderWatchedOn = history[0]?.providerWatchedOn;
 	const watchProviders = useGetWatchProviders(metadataDetails.lot);
 
@@ -1118,19 +1131,32 @@ const NewProgressUpdateForm = ({
 		<Form
 			method="POST"
 			onSubmit={onSubmit}
-			action={withQuery($path("/actions"), { intent: "progressUpdate" })}
+			action={withQuery($path("/actions"), {
+				intent:
+					watchTime === WatchTimes.JustStartedIt
+						? "individualProgressUpdate"
+						: "progressUpdate",
+			})}
 		>
 			{[
 				...Object.entries(metadataToUpdate),
-				["metadataLot", metadataDetails.lot],
-			].map(([k, v]) => (
-				<Fragment key={k}>
-					{typeof v !== "undefined" ? (
-						<input hidden readOnly name={k} value={v?.toString()} />
-					) : null}
-				</Fragment>
-			))}
-			<Stack>
+				watchTime !== WatchTimes.JustStartedIt
+					? ["metadataLot", metadataDetails.lot]
+					: undefined,
+				watchTime === WatchTimes.JustStartedIt ? ["progress", "0"] : undefined,
+				selectedDate
+					? ["date", formatDateToNaiveDate(selectedDate)]
+					: undefined,
+			]
+				.filter((v) => typeof v !== "undefined")
+				.map(([k, v]) => (
+					<Fragment key={k}>
+						{typeof v !== "undefined" ? (
+							<input hidden readOnly name={k} value={v?.toString()} />
+						) : null}
+					</Fragment>
+				))}
+			<Stack ref={parent}>
 				{metadataDetails.lot === MediaLot.Anime ? (
 					<>
 						<NumberInput
@@ -1281,18 +1307,34 @@ const NewProgressUpdateForm = ({
 					</>
 				) : null}
 				<Select
-					label={`When did you ${getVerb(Verb.Read, metadataDetails.lot)} it?`}
-					data={WATCH_TIMES}
 					value={watchTime}
+					data={Object.values(WatchTimes).filter((v) =>
+						[
+							MediaLot.Show,
+							MediaLot.Podcast,
+							MediaLot.Anime,
+							MediaLot.Manga,
+						].includes(metadataDetails.lot)
+							? v !== WatchTimes.JustStartedIt
+							: true,
+					)}
+					label={`When did you ${getVerb(Verb.Read, metadataDetails.lot)} it?`}
 					onChange={(v) => {
 						setWatchTime(v as typeof watchTime);
 						match(v)
-							.with(WATCH_TIMES[0], () => setSelectedDate(new Date()))
-							.with(WATCH_TIMES[1], () => setSelectedDate(null))
-							.with(WATCH_TIMES[2], () => setSelectedDate(null));
+							.with(WatchTimes.JustCompletedNow, () =>
+								setSelectedDate(new Date()),
+							)
+							.with(
+								WatchTimes.IDontRemember,
+								WatchTimes.CustomDate,
+								WatchTimes.JustStartedIt,
+								() => setSelectedDate(null),
+							)
+							.run();
 					}}
 				/>
-				{watchTime === WATCH_TIMES[2] ? (
+				{watchTime === WatchTimes.CustomDate ? (
 					<DatePickerInput
 						required
 						clearable
@@ -1302,18 +1344,12 @@ const NewProgressUpdateForm = ({
 						label="Enter exact date"
 					/>
 				) : null}
-				<Select
-					data={watchProviders}
-					name="providerWatchedOn"
-					defaultValue={lastProviderWatchedOn}
-					label={`Where did you ${getVerb(Verb.Read, metadataDetails.lot)} it?`}
-				/>
-				{selectedDate ? (
-					<input
-						hidden
-						readOnly
-						name="date"
-						value={formatDateToNaiveDate(selectedDate)}
+				{watchTime !== WatchTimes.JustStartedIt ? (
+					<Select
+						data={watchProviders}
+						name="providerWatchedOn"
+						defaultValue={lastProviderWatchedOn}
+						label={`Where did you ${getVerb(Verb.Read, metadataDetails.lot)} it?`}
 					/>
 				) : null}
 				<Button
diff --git a/apps/frontend/app/routes/auth.tsx b/apps/frontend/app/routes/auth.tsx
index ce12194341..e2576456fb 100644
--- a/apps/frontend/app/routes/auth.tsx
+++ b/apps/frontend/app/routes/auth.tsx
@@ -20,7 +20,6 @@ import {
 import { Form, Link, useLoaderData, useSearchParams } from "@remix-run/react";
 import {
 	GetOidcRedirectUrlDocument,
-	LatestUserSummaryDocument,
 	LoginErrorVariant,
 	LoginUserDocument,
 	MediaLot,
@@ -40,7 +39,6 @@ import {
 	createToastHeaders,
 	getAuthorizationCookie,
 	getCachedCoreDetails,
-	getCachedUserPreferences,
 	getCookiesForApplication,
 	redirectWithToast,
 	serverGqlService,
@@ -57,27 +55,18 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const query = zx.parseQuery(request, searchParamsSchema);
 	const isAuthenticated = !!getAuthorizationCookie(request);
 	if (isAuthenticated) {
-		const [userPreferences, { latestUserSummary }] = await Promise.all([
-			getCachedUserPreferences(request),
-			serverGqlService.authenticatedRequest(request, LatestUserSummaryDocument),
-		]);
-		if (
-			latestUserSummary.totalMetadataCount === 0 &&
-			userPreferences.featuresEnabled.media.enabled === true
-		)
-			throw await redirectWithToast(
-				$path(
-					"/media/:action/:lot",
-					{ action: "search", lot: MediaLot.Movie },
-					{ query: "avengers" },
-				),
-				{
-					message:
-						"Welcome to Ryot! Get started by adding a movie to your watchlist!",
-					closeAfter: dayjsLib.duration(10, "second").asMilliseconds(),
-				},
-			);
-		throw redirect($path("/"));
+		throw await redirectWithToast(
+			$path(
+				"/media/:action/:lot",
+				{ action: "search", lot: MediaLot.Movie },
+				{ query: "avengers" },
+			),
+			{
+				message:
+					"Welcome to Ryot! Get started by adding a movie to your watchlist!",
+				closeAfter: dayjsLib.duration(10, "second").asMilliseconds(),
+			},
+		);
 	}
 	const [coreDetails] = await Promise.all([getCachedCoreDetails()]);
 	return {
diff --git a/apps/frontend/app/styles/common.module.css b/apps/frontend/app/styles/common.module.css
index 7ae46d48be..fdf826746d 100644
--- a/apps/frontend/app/styles/common.module.css
+++ b/apps/frontend/app/styles/common.module.css
@@ -1,7 +1,7 @@
 .starIcon {
-  color: #9CA3AF;
+  color: #9ca3af;
   &:hover {
-    color: #eBE600FF;
+    color: #ebe600ff;
   }
 }
 
@@ -19,8 +19,11 @@
   }
 }
 
-.mediaImage {
-  &:hover {
-    box-shadow: 0 0 10px black;
+.highlightImage {
+  @mixin dark {
+    box-shadow: 0px 0px 4px 1px rgba(242, 183, 22, 1);
+  }
+  @mixin light {
+    box-shadow: 0px 0px 8px 3px rgba(24, 142, 245, 1);
   }
 }
diff --git a/apps/frontend/package.json b/apps/frontend/package.json
index 67af1d0587..13bf70c04b 100644
--- a/apps/frontend/package.json
+++ b/apps/frontend/package.json
@@ -28,19 +28,20 @@
 		"@ryot/graphql": "workspace:*",
 		"@ryot/ts-utils": "workspace:*",
 		"@tabler/icons-react": "3.17.0",
-		"@tanstack/react-query": "5.62.0",
-		"@tanstack/react-query-devtools": "5.62.0",
+		"@tanstack/react-query": "5.62.2",
+		"@tanstack/react-query-devtools": "5.62.2",
 		"buffer": "6.0.3",
 		"clsx": "2.1.1",
 		"cookie": "1.0.2",
 		"crypto-js": "4.2.0",
 		"dayjs": "1.11.13",
-		"dotenv": "16.4.5",
+		"dotenv": "16.4.7",
 		"embla-carousel-react": "7.1.0",
 		"filesize": "10.1.6",
 		"graphql": "16.9.0",
 		"graphql-request": "7.1.2",
 		"howler": "2.2.4",
+		"html2canvas": "1.4.1",
 		"humanize-duration-ts": "2.1.1",
 		"immer": "10.1.1",
 		"isbot": "5.1.17",
@@ -52,9 +53,9 @@
 		"react": "18.3.1",
 		"react-confirm": "0.3.0-7",
 		"react-dom": "18.3.1",
-		"react-virtuoso": "4.12.2",
+		"react-virtuoso": "4.12.3",
 		"react-webcam": "7.2.0",
-		"recharts": "2.13.3",
+		"recharts": "2.14.1",
 		"remix-routes": "1.7.7",
 		"remix-utils": "7.7.0",
 		"tailwind-merge": "2.5.5",
diff --git a/apps/website/package.json b/apps/website/package.json
index aa8db7439c..cc565fc845 100644
--- a/apps/website/package.json
+++ b/apps/website/package.json
@@ -14,7 +14,7 @@
 		"@radix-ui/react-label": "2.1.0",
 		"@radix-ui/react-slot": "1.1.0",
 		"@radix-ui/react-tooltip": "1.1.4",
-		"@react-email/components": "0.0.28",
+		"@react-email/components": "0.0.29",
 		"@remix-run/node": "2.15.0",
 		"@remix-run/react": "2.15.0",
 		"@remix-run/serve": "2.15.0",
@@ -28,7 +28,7 @@
 		"clsx": "2.1.1",
 		"crypto-js": "4.2.0",
 		"dayjs": "1.11.13",
-		"drizzle-orm": "0.36.4",
+		"drizzle-orm": "0.37.0",
 		"embla-carousel-autoplay": "8.5.1",
 		"embla-carousel-react": "8.5.1",
 		"graphql": "16.9.0",
@@ -37,7 +37,7 @@
 		"input-otp": "1.4.1",
 		"isbot": "5.1.17",
 		"lodash": "4.17.21",
-		"lucide-react": "0.462.0",
+		"lucide-react": "0.465.0",
 		"nanoid": "5.0.9",
 		"next-themes": "0.4.3",
 		"nodemailer": "6.9.16",
@@ -65,9 +65,9 @@
 		"@types/react": "18.3.12",
 		"@types/react-dom": "18.3.1",
 		"autoprefixer": "10.4.20",
-		"drizzle-kit": "0.28.1",
+		"drizzle-kit": "0.29.1",
 		"postcss": "8.4.49",
-		"tailwindcss": "3.4.15",
+		"tailwindcss": "3.4.16",
 		"typescript": "5.7.2",
 		"typescript-remix-routes-plugin": "1.0.1",
 		"vite": "5.4.11",
diff --git a/crates/background/src/lib.rs b/crates/background/src/lib.rs
index 3b0cdb57dc..01fd0824e5 100644
--- a/crates/background/src/lib.rs
+++ b/crates/background/src/lib.rs
@@ -24,22 +24,23 @@ impl Message for CoreApplicationJob {
 // The background jobs which can be deployed by the application.
 #[derive(Debug, Deserialize, Serialize, Display)]
 pub enum ApplicationJob {
-    ImportFromExternalSource(String, Box<DeployImportJobInput>),
-    ReEvaluateUserWorkouts(String),
-    UpdateMetadata(String, bool),
-    UpdateGithubExerciseJob(GithubExercise),
     UpdatePerson(String),
-    UpdateMetadataGroup(String),
-    RecalculateCalendarEvents,
-    AssociateGroupWithMetadata(MediaLot, MediaSource, String),
+    SyncIntegrationsData,
+    UpdateExerciseLibrary,
     PerformExport(String),
-    RecalculateUserActivitiesAndSummary(String, bool),
     PerformBackgroundTasks,
-    UpdateExerciseLibrary,
-    SyncIntegrationsData,
-    HandleEntityAddedToCollectionEvent(Uuid),
-    HandleAfterMediaSeenTasks(seen::Model),
+    RecalculateCalendarEvents,
+    ReviseUserWorkouts(String),
+    PerformServerKeyValidation,
+    UpdateMetadataGroup(String),
+    UpdateMetadata(String, bool),
     HandleOnSeenComplete(String),
+    HandleAfterMediaSeenTasks(seen::Model),
+    UpdateGithubExerciseJob(GithubExercise),
+    HandleEntityAddedToCollectionEvent(Uuid),
+    RecalculateUserActivitiesAndSummary(String, bool),
+    AssociateGroupWithMetadata(MediaLot, MediaSource, String),
+    ImportFromExternalSource(String, Box<DeployImportJobInput>),
 }
 
 impl Message for ApplicationJob {
diff --git a/crates/enums/src/lib.rs b/crates/enums/src/lib.rs
index 8a0f89ce21..12a333f2e1 100644
--- a/crates/enums/src/lib.rs
+++ b/crates/enums/src/lib.rs
@@ -220,16 +220,19 @@ pub enum ImportSource {
     Enum,
     Copy,
     Deserialize,
-    FromJsonQueryResult,
+    DeriveActiveEnum,
+    EnumIter,
     Eq,
     PartialEq,
-    EnumIter,
-    PartialOrd,
-    Ord,
     Default,
     ConfigEnum,
     Hash,
 )]
+#[sea_orm(
+    rs_type = "String",
+    db_type = "String(StringLen::None)",
+    rename_all = "snake_case"
+)]
 #[serde(rename_all = "snake_case")]
 pub enum ExerciseMuscle {
     #[default]
@@ -435,17 +438,17 @@ pub enum ExerciseSource {
 
 /// The different types of personal bests that can be achieved on a set.
 #[derive(
-    Clone,
-    Debug,
-    Deserialize,
-    Serialize,
-    FromJsonQueryResult,
     Eq,
-    PartialEq,
     Enum,
     Copy,
+    Clone,
+    Debug,
     Default,
+    PartialEq,
+    Serialize,
     ConfigEnum,
+    Deserialize,
+    FromJsonQueryResult,
 )]
 #[serde(rename_all = "snake_case")]
 pub enum WorkoutSetPersonalBest {
@@ -548,16 +551,17 @@ pub enum IntegrationLot {
 )]
 #[serde(rename_all = "snake_case")]
 pub enum IntegrationProvider {
-    Audiobookshelf,
-    JellyfinSink,
     Emby,
-    Plex,
     Kodi,
+    Komga,
     Radarr,
     Sonarr,
-    JellyfinPush,
-    Komga,
+    PlexSink,
+    PlexYank,
     GenericJson,
+    JellyfinPush,
+    JellyfinSink,
+    Audiobookshelf,
 }
 
 #[derive(
diff --git a/crates/migrations/src/lib.rs b/crates/migrations/src/lib.rs
index 42fc2195ea..bb7e56261d 100644
--- a/crates/migrations/src/lib.rs
+++ b/crates/migrations/src/lib.rs
@@ -50,6 +50,7 @@ mod m20241025_changes_for_issue_1084;
 mod m20241110_changes_for_issue_1103;
 mod m20241121_changes_for_issue_445;
 mod m20241124_changes_for_issue_1118;
+mod m20241126_changes_for_issue_1113;
 mod m20241129_changes_for_issue_1114;
 
 pub use m20230410_create_metadata::Metadata as AliasedMetadata;
@@ -123,6 +124,7 @@ impl MigratorTrait for Migrator {
             Box::new(m20241121_changes_for_issue_445::Migration),
             Box::new(m20241124_changes_for_issue_1118::Migration),
             Box::new(m20241129_changes_for_issue_1114::Migration),
+            Box::new(m20241126_changes_for_issue_1113::Migration),
         ]
     }
 }
diff --git a/crates/migrations/src/m20230417_create_user.rs b/crates/migrations/src/m20230417_create_user.rs
index 3bf2b9205f..501b7ad214 100644
--- a/crates/migrations/src/m20230417_create_user.rs
+++ b/crates/migrations/src/m20230417_create_user.rs
@@ -5,17 +5,17 @@ pub struct Migration;
 
 #[derive(Iden)]
 pub enum User {
-    Table,
     Id,
-    CreatedOn,
-    LastLoginOn,
+    Lot,
     Name,
+    Table,
     Password,
-    Lot,
+    CreatedOn,
+    IsDisabled,
+    LastLoginOn,
     Preferences,
     OidcIssuerId,
     ExtraInformation,
-    IsDisabled,
 }
 
 #[async_trait::async_trait]
diff --git a/crates/migrations/src/m20230822_create_exercise.rs b/crates/migrations/src/m20230822_create_exercise.rs
index 7b1d582f74..75d1704657 100644
--- a/crates/migrations/src/m20230822_create_exercise.rs
+++ b/crates/migrations/src/m20230822_create_exercise.rs
@@ -6,19 +6,21 @@ use super::m20230417_create_user::User;
 #[derive(DeriveMigrationName)]
 pub struct Migration;
 
+pub static EXERCISE_NAME_INDEX: &str = "exercise__name__index";
+
 #[derive(Iden)]
 pub enum Exercise {
     Table,
     Id,
     Lot,
+    Name,
     Force,
     Level,
+    Source,
+    Muscles,
     Mechanic,
     Equipment,
-    Muscles,
-    Identifier,
     Attributes,
-    Source,
     CreatedByUserId,
 }
 
@@ -30,13 +32,12 @@ impl MigrationTrait for Migration {
                 Table::create()
                     .table(Exercise::Table)
                     .col(ColumnDef::new(Exercise::Id).primary_key().text().not_null())
-                    .col(ColumnDef::new(Exercise::Muscles).json_binary().not_null())
                     .col(ColumnDef::new(Exercise::Lot).text().not_null())
                     .col(ColumnDef::new(Exercise::Level).text().not_null())
                     .col(ColumnDef::new(Exercise::Force).text())
                     .col(ColumnDef::new(Exercise::Mechanic).text())
                     .col(ColumnDef::new(Exercise::Equipment).text())
-                    .col(ColumnDef::new(Exercise::Identifier).text())
+                    .col(ColumnDef::new(Exercise::Name).text())
                     .col(
                         ColumnDef::new(Exercise::Attributes)
                             .json_binary()
@@ -44,6 +45,11 @@ impl MigrationTrait for Migration {
                     )
                     .col(ColumnDef::new(Exercise::Source).text().not_null())
                     .col(ColumnDef::new(Exercise::CreatedByUserId).text())
+                    .col(
+                        ColumnDef::new(Exercise::Muscles)
+                            .array(ColumnType::Text)
+                            .not_null(),
+                    )
                     .foreign_key(
                         ForeignKey::create()
                             .name("workout_to_user_foreign_key")
@@ -58,10 +64,9 @@ impl MigrationTrait for Migration {
         manager
             .create_index(
                 Index::create()
-                    .unique()
-                    .name("exercise__identifier__index")
+                    .name(EXERCISE_NAME_INDEX)
                     .table(Exercise::Table)
-                    .col(Exercise::Identifier)
+                    .col(Exercise::Name)
                     .to_owned(),
             )
             .await?;
diff --git a/crates/migrations/src/m20240827_create_daily_user_activity.rs b/crates/migrations/src/m20240827_create_daily_user_activity.rs
index 48c8f58b2a..172d0f4876 100644
--- a/crates/migrations/src/m20240827_create_daily_user_activity.rs
+++ b/crates/migrations/src/m20240827_create_daily_user_activity.rs
@@ -6,7 +6,7 @@ fn integer_not_null<T: IntoIden>(col: T) -> ColumnDef {
     ColumnDef::new(col).integer().not_null().default(0).take()
 }
 
-pub static DAILY_USER_ACTIVITY_PRIMARY_KEY: &str = "pk-daily_user_activity";
+pub static DAILY_USER_ACTIVITY_COMPOSITE_UNIQUE_KEY: &str = "daily_user_activity_uqi1";
 
 #[derive(DeriveMigrationName)]
 pub struct Migration;
@@ -15,6 +15,7 @@ pub struct Migration;
 pub enum DailyUserActivity {
     Table,
     UserId,
+    Id,
     Date,
     EntityIds,
     MetadataReviewCount,
@@ -52,90 +53,129 @@ pub enum DailyUserActivity {
     TotalCount,
     TotalDuration,
     HourRecords,
+    WorkoutMuscles,
+    WorkoutExercises,
+    WorkoutEquipments,
+}
+
+// FIXME: Remove this function and move it to the main function.
+pub async fn create_daily_user_activity_table(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
+    manager
+        .create_table(
+            Table::create()
+                .table(DailyUserActivity::Table)
+                .col(
+                    ColumnDef::new(DailyUserActivity::Id)
+                        .uuid()
+                        .not_null()
+                        .default(PgFunc::gen_random_uuid())
+                        .primary_key(),
+                )
+                .col(ColumnDef::new(DailyUserActivity::Date).date())
+                .col(ColumnDef::new(DailyUserActivity::UserId).text().not_null())
+                .col(
+                    ColumnDef::new(DailyUserActivity::EntityIds)
+                        .array(ColumnType::Text)
+                        .not_null()
+                        .default(Expr::cust("'{}'")),
+                )
+                .col(integer_not_null(DailyUserActivity::MetadataReviewCount))
+                .col(integer_not_null(DailyUserActivity::CollectionReviewCount))
+                .col(integer_not_null(
+                    DailyUserActivity::MetadataGroupReviewCount,
+                ))
+                .col(integer_not_null(DailyUserActivity::PersonReviewCount))
+                .col(integer_not_null(DailyUserActivity::ExerciseReviewCount))
+                .col(integer_not_null(DailyUserActivity::WorkoutCount))
+                .col(integer_not_null(DailyUserActivity::WorkoutDuration))
+                .col(integer_not_null(DailyUserActivity::MeasurementCount))
+                .col(integer_not_null(DailyUserActivity::AudioBookCount))
+                .col(integer_not_null(DailyUserActivity::AudioBookDuration))
+                .col(integer_not_null(DailyUserActivity::AnimeCount))
+                .col(integer_not_null(DailyUserActivity::BookCount))
+                .col(integer_not_null(DailyUserActivity::BookPages))
+                .col(integer_not_null(DailyUserActivity::PodcastCount))
+                .col(integer_not_null(DailyUserActivity::PodcastDuration))
+                .col(integer_not_null(DailyUserActivity::MangaCount))
+                .col(integer_not_null(DailyUserActivity::MovieCount))
+                .col(integer_not_null(DailyUserActivity::MovieDuration))
+                .col(integer_not_null(DailyUserActivity::ShowCount))
+                .col(integer_not_null(DailyUserActivity::ShowDuration))
+                .col(integer_not_null(DailyUserActivity::VideoGameCount))
+                .col(integer_not_null(DailyUserActivity::VideoGameDuration))
+                .col(integer_not_null(DailyUserActivity::VisualNovelCount))
+                .col(integer_not_null(DailyUserActivity::VisualNovelDuration))
+                .col(integer_not_null(DailyUserActivity::WorkoutPersonalBests))
+                .col(integer_not_null(DailyUserActivity::WorkoutWeight))
+                .col(integer_not_null(DailyUserActivity::WorkoutReps))
+                .col(integer_not_null(DailyUserActivity::WorkoutDistance))
+                .col(integer_not_null(DailyUserActivity::WorkoutRestTime))
+                .col(integer_not_null(DailyUserActivity::TotalMetadataCount))
+                .col(integer_not_null(DailyUserActivity::TotalReviewCount))
+                .col(integer_not_null(DailyUserActivity::TotalCount))
+                .col(integer_not_null(DailyUserActivity::TotalDuration))
+                .col(
+                    ColumnDef::new(DailyUserActivity::HourRecords)
+                        .json_binary()
+                        .not_null()
+                        .default(Expr::cust("'[]'")),
+                )
+                .col(
+                    ColumnDef::new(DailyUserActivity::WorkoutMuscles)
+                        .array(ColumnType::Text)
+                        .not_null()
+                        .default(Expr::cust("'{}'")),
+                )
+                .col(
+                    ColumnDef::new(DailyUserActivity::WorkoutExercises)
+                        .array(ColumnType::Text)
+                        .not_null()
+                        .default(Expr::cust("'{}'")),
+                )
+                .col(
+                    ColumnDef::new(DailyUserActivity::WorkoutEquipments)
+                        .array(ColumnType::Text)
+                        .not_null()
+                        .default(Expr::cust("'{}'")),
+                )
+                .foreign_key(
+                    ForeignKey::create()
+                        .name("daily_user_activity_to_user_foreign_key")
+                        .from(DailyUserActivity::Table, DailyUserActivity::UserId)
+                        .to(User::Table, User::Id)
+                        .on_delete(ForeignKeyAction::Cascade)
+                        .on_update(ForeignKeyAction::Cascade),
+                )
+                .to_owned(),
+        )
+        .await?;
+    manager
+        .create_index(
+            Index::create()
+                .name(DAILY_USER_ACTIVITY_COMPOSITE_UNIQUE_KEY)
+                .unique()
+                .table(DailyUserActivity::Table)
+                .col(DailyUserActivity::UserId)
+                .col(DailyUserActivity::Date)
+                .to_owned(),
+        )
+        .await?;
+    manager
+        .create_index(
+            Index::create()
+                .name("daily_user_activity-user_id__index")
+                .table(DailyUserActivity::Table)
+                .col(DailyUserActivity::UserId)
+                .to_owned(),
+        )
+        .await?;
+    Ok(())
 }
 
 #[async_trait::async_trait]
 impl MigrationTrait for Migration {
     async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
-        manager
-            .create_table(
-                Table::create()
-                    .table(DailyUserActivity::Table)
-                    .col(ColumnDef::new(DailyUserActivity::Date).date().not_null())
-                    .col(ColumnDef::new(DailyUserActivity::UserId).text().not_null())
-                    .col(
-                        ColumnDef::new(DailyUserActivity::EntityIds)
-                            .array(ColumnType::Text)
-                            .not_null()
-                            .default(Expr::cust("'{}'")),
-                    )
-                    .primary_key(
-                        Index::create()
-                            .name(DAILY_USER_ACTIVITY_PRIMARY_KEY)
-                            .col(DailyUserActivity::Date)
-                            .col(DailyUserActivity::UserId),
-                    )
-                    .col(integer_not_null(DailyUserActivity::MetadataReviewCount))
-                    .col(integer_not_null(DailyUserActivity::CollectionReviewCount))
-                    .col(integer_not_null(
-                        DailyUserActivity::MetadataGroupReviewCount,
-                    ))
-                    .col(integer_not_null(DailyUserActivity::PersonReviewCount))
-                    .col(integer_not_null(DailyUserActivity::ExerciseReviewCount))
-                    .col(integer_not_null(DailyUserActivity::WorkoutCount))
-                    .col(integer_not_null(DailyUserActivity::WorkoutDuration))
-                    .col(integer_not_null(DailyUserActivity::MeasurementCount))
-                    .col(integer_not_null(DailyUserActivity::AudioBookCount))
-                    .col(integer_not_null(DailyUserActivity::AudioBookDuration))
-                    .col(integer_not_null(DailyUserActivity::AnimeCount))
-                    .col(integer_not_null(DailyUserActivity::BookCount))
-                    .col(integer_not_null(DailyUserActivity::BookPages))
-                    .col(integer_not_null(DailyUserActivity::PodcastCount))
-                    .col(integer_not_null(DailyUserActivity::PodcastDuration))
-                    .col(integer_not_null(DailyUserActivity::MangaCount))
-                    .col(integer_not_null(DailyUserActivity::MovieCount))
-                    .col(integer_not_null(DailyUserActivity::MovieDuration))
-                    .col(integer_not_null(DailyUserActivity::ShowCount))
-                    .col(integer_not_null(DailyUserActivity::ShowDuration))
-                    .col(integer_not_null(DailyUserActivity::VideoGameCount))
-                    .col(integer_not_null(DailyUserActivity::VideoGameDuration))
-                    .col(integer_not_null(DailyUserActivity::VisualNovelCount))
-                    .col(integer_not_null(DailyUserActivity::VisualNovelDuration))
-                    .col(integer_not_null(DailyUserActivity::WorkoutPersonalBests))
-                    .col(integer_not_null(DailyUserActivity::WorkoutWeight))
-                    .col(integer_not_null(DailyUserActivity::WorkoutReps))
-                    .col(integer_not_null(DailyUserActivity::WorkoutDistance))
-                    .col(integer_not_null(DailyUserActivity::WorkoutRestTime))
-                    .col(integer_not_null(DailyUserActivity::TotalMetadataCount))
-                    .col(integer_not_null(DailyUserActivity::TotalReviewCount))
-                    .col(integer_not_null(DailyUserActivity::TotalCount))
-                    .col(integer_not_null(DailyUserActivity::TotalDuration))
-                    .col(
-                        ColumnDef::new(DailyUserActivity::HourRecords)
-                            .json_binary()
-                            .not_null()
-                            .default(Expr::cust("'[]'")),
-                    )
-                    .foreign_key(
-                        ForeignKey::create()
-                            .name("daily_user_activity_to_user_foreign_key")
-                            .from(DailyUserActivity::Table, DailyUserActivity::UserId)
-                            .to(User::Table, User::Id)
-                            .on_delete(ForeignKeyAction::Cascade)
-                            .on_update(ForeignKeyAction::Cascade),
-                    )
-                    .to_owned(),
-            )
-            .await?;
-        manager
-            .create_index(
-                Index::create()
-                    .name("daily_user_activity-user_id__index")
-                    .table(DailyUserActivity::Table)
-                    .col(DailyUserActivity::UserId)
-                    .to_owned(),
-            )
-            .await?;
+        create_daily_user_activity_table(manager).await?;
         Ok(())
     }
 
diff --git a/crates/migrations/src/m20241004_create_application_cache.rs b/crates/migrations/src/m20241004_create_application_cache.rs
index 1fb661360e..718c823d98 100644
--- a/crates/migrations/src/m20241004_create_application_cache.rs
+++ b/crates/migrations/src/m20241004_create_application_cache.rs
@@ -8,6 +8,7 @@ pub enum ApplicationCache {
     Table,
     Id,
     Key,
+    Value,
     ExpiresAt,
     CreatedAt,
 }
@@ -39,6 +40,11 @@ impl MigrationTrait for Migration {
                             .unique_key(),
                     )
                     .col(ColumnDef::new(ApplicationCache::ExpiresAt).timestamp_with_time_zone())
+                    .col(
+                        ColumnDef::new(ApplicationCache::Value)
+                            .json_binary()
+                            .not_null(),
+                    )
                     .to_owned(),
             )
             .await?;
diff --git a/crates/migrations/src/m20241126_changes_for_issue_1113.rs b/crates/migrations/src/m20241126_changes_for_issue_1113.rs
new file mode 100644
index 0000000000..3a18b387a8
--- /dev/null
+++ b/crates/migrations/src/m20241126_changes_for_issue_1113.rs
@@ -0,0 +1,152 @@
+use sea_orm_migration::prelude::*;
+
+use crate::{
+    m20230822_create_exercise::EXERCISE_NAME_INDEX,
+    m20240827_create_daily_user_activity::create_daily_user_activity_table,
+};
+
+#[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("daily_user_activity", "id").await? {
+            db.execute_unprepared("DROP TABLE daily_user_activity")
+                .await?;
+            create_daily_user_activity_table(manager).await?;
+        }
+        if !manager.has_column("exercise", "name").await? {
+            db.execute_unprepared(&format!(
+                r#"
+UPDATE exercise SET identifier = id;
+ALTER TABLE exercise RENAME COLUMN identifier TO name;
+DROP INDEX "exercise__identifier__index";
+CREATE INDEX "{}" ON "exercise" ("name");
+
+UPDATE workout
+SET information =
+    JSONB_SET(
+        information,
+        '{{exercises}}',
+        (
+            SELECT JSONB_AGG(
+                JSONB_SET(
+                    exercise,
+                    '{{id}}',
+                    exercise->'name',
+                    true
+                ) - 'name'
+            )
+            FROM JSONB_ARRAY_ELEMENTS(information->'exercises') AS exercise
+        )
+    ),
+    summary =
+    JSONB_SET(
+        summary,
+        '{{exercises}}',
+        (
+            SELECT JSONB_AGG(
+                JSONB_SET(
+                    exercise,
+                    '{{id}}',
+                    exercise->'name',
+                    true
+                ) - 'name'
+            )
+            FROM JSONB_ARRAY_ELEMENTS(summary->'exercises') AS exercise
+        )
+    );
+            "#,
+                EXERCISE_NAME_INDEX
+            ))
+            .await?;
+        }
+        db.execute_unprepared(
+            r#"
+DO $$
+BEGIN
+    IF EXISTS (
+        SELECT 1
+        FROM information_schema.columns
+        WHERE table_name = 'exercise'
+          AND column_name = 'muscles'
+          AND data_type = 'jsonb'
+    ) THEN
+        ALTER TABLE exercise ADD COLUMN muscles_text_array text[];
+        UPDATE exercise
+        SET muscles_text_array = ARRAY(SELECT jsonb_array_elements_text(muscles));
+        ALTER TABLE exercise DROP COLUMN muscles;
+        ALTER TABLE exercise RENAME COLUMN muscles_text_array TO muscles;
+    END IF;
+END $$;
+        "#,
+        )
+        .await?;
+        if !manager.has_column("application_cache", "value").await? {
+            db.execute_unprepared(
+                r#"
+ALTER TABLE "application_cache" ADD COLUMN "value" JSONB;
+UPDATE "application_cache" SET "value" = '"Empty"'::jsonb;
+ALTER TABLE "application_cache" ALTER COLUMN "value" SET NOT NULL;
+"#,
+            )
+            .await?;
+        }
+        db.execute_unprepared(
+            r#"
+UPDATE "user" SET "extra_information" = JSONB_BUILD_OBJECT('scheduled_for_workout_revision', true);
+        "#,
+        )
+        .await?;
+        db.execute_unprepared(
+            r#"
+UPDATE "user" SET "preferences" = jsonb_set(
+    "preferences", '{features_enabled,analytics}',
+    JSONB_BUILD_OBJECT('enabled', true)
+);
+        "#,
+        )
+        .await?;
+        db.execute_unprepared(
+            r#"
+UPDATE "integration" SET "provider" = 'plex_sink' WHERE "provider" = 'plex';
+UPDATE "integration" SET "provider_specifics" = jsonb_set(
+    "provider_specifics",
+    '{plex_sink_username}',
+    "provider_specifics"->'plex_username'
+) - 'plex_username' WHERE "provider_specifics" -> 'plex_username' IS NOT NULL;
+            "#,
+        )
+        .await?;
+        db.execute_unprepared(
+            r#"
+UPDATE "user_to_entity" SET "exercise_extra_information" = jsonb_set("exercise_extra_information", '{settings,exclude_from_analytics}', 'false')
+WHERE "exercise_extra_information" IS NOT NULL;
+            "#,
+        )
+        .await?;
+        db.execute_unprepared(
+            r#"
+UPDATE "user"
+SET preferences =
+    jsonb_set(
+        preferences,
+        '{general, dashboard}',
+        (
+            SELECT jsonb_agg(element)
+            FROM jsonb_array_elements(preferences->'general'->'dashboard') AS element
+            WHERE element->>'section' != 'ACTIVITY'
+        )
+    );
+            "#,
+        )
+        .await?;
+        Ok(())
+    }
+
+    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
+        Ok(())
+    }
+}
diff --git a/crates/models/common/Cargo.toml b/crates/models/common/Cargo.toml
index 780195cb14..5c8ca4646c 100644
--- a/crates/models/common/Cargo.toml
+++ b/crates/models/common/Cargo.toml
@@ -5,6 +5,7 @@ edition = "2021"
 
 [dependencies]
 async-graphql = { workspace = true }
+chrono = { workspace = true }
 educe = { workspace = true }
 enums = { path = "../../enums" }
 enum_meta = { workspace = true }
diff --git a/crates/models/common/src/lib.rs b/crates/models/common/src/lib.rs
index 02043c8fb3..6306a167c6 100644
--- a/crates/models/common/src/lib.rs
+++ b/crates/models/common/src/lib.rs
@@ -1,10 +1,13 @@
+use std::collections::HashMap;
+
 use async_graphql::{Enum, InputObject, SimpleObject};
+use chrono::NaiveDate;
 use educe::Educe;
 use enum_meta::{meta, Meta};
-use enums::{EntityLot, MediaLot};
+use enums::{EntityLot, ExerciseEquipment, ExerciseMuscle, MediaLot};
 use rust_decimal::Decimal;
 use schematic::{ConfigEnum, Schematic};
-use sea_orm::{prelude::DateTimeUtc, FromJsonQueryResult};
+use sea_orm::{prelude::DateTimeUtc, FromJsonQueryResult, FromQueryResult};
 use serde::{Deserialize, Serialize};
 use serde_with::skip_serializing_none;
 use strum::{Display, EnumIter};
@@ -134,10 +137,10 @@ meta! {
 #[derive(Enum, Serialize, Deserialize, Clone, Debug, Copy, PartialEq, Eq)]
 pub enum BackgroundJob {
     UpdateAllMetadata,
+    ReviseUserWorkouts,
     UpdateAllExercises,
     SyncIntegrationsData,
     PerformBackgroundTasks,
-    ReEvaluateUserWorkouts,
     RecalculateCalendarEvents,
     CalculateUserActivitiesAndSummary,
 }
@@ -202,20 +205,6 @@ pub struct ExportJob {
 }
 
 #[skip_serializing_none]
-#[derive(Clone, Debug, PartialEq, FromJsonQueryResult, Eq, Serialize, Deserialize)]
-pub enum ApplicationCacheKey {
-    ProgressUpdateCache {
-        user_id: String,
-        metadata_id: String,
-        show_season_number: Option<i32>,
-        show_episode_number: Option<i32>,
-        podcast_episode_number: Option<i32>,
-        anime_episode_number: Option<i32>,
-        manga_chapter_number: Option<Decimal>,
-        manga_volume_number: Option<i32>,
-    },
-}
-
 #[derive(
     Debug, PartialEq, Eq, Serialize, Deserialize, Clone, SimpleObject, FromJsonQueryResult,
 )]
@@ -225,6 +214,7 @@ pub struct DailyUserActivityHourRecordEntity {
     pub metadata_lot: Option<MediaLot>,
 }
 
+#[skip_serializing_none]
 #[derive(
     Debug, PartialEq, Eq, Serialize, Deserialize, Clone, SimpleObject, FromJsonQueryResult,
 )]
@@ -233,3 +223,189 @@ pub struct DailyUserActivityHourRecord {
     pub hour: u32,
     pub entities: Vec<DailyUserActivityHourRecordEntity>,
 }
+
+/// The start date must be before the end date.
+#[skip_serializing_none]
+#[derive(
+    Debug, Default, Serialize, Deserialize, SimpleObject, InputObject, Clone, Eq, PartialEq,
+)]
+#[graphql(input_name = "ApplicationDateRangeInput")]
+pub struct ApplicationDateRange {
+    pub end_date: Option<NaiveDate>,
+    pub start_date: Option<NaiveDate>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Enum, Clone, Copy, Eq, PartialEq, Display)]
+#[strum(serialize_all = "snake_case")]
+pub enum DailyUserActivitiesResponseGroupedBy {
+    Day,
+    Month,
+    Year,
+    Millennium,
+}
+
+#[skip_serializing_none]
+#[derive(Debug, Default, Serialize, Deserialize, InputObject, Clone, PartialEq, Eq)]
+pub struct UserAnalyticsInput {
+    pub date_range: ApplicationDateRange,
+    pub group_by: Option<DailyUserActivitiesResponseGroupedBy>,
+}
+
+#[skip_serializing_none]
+#[derive(
+    Debug, Default, SimpleObject, Serialize, Deserialize, Clone, FromQueryResult, PartialEq, Eq,
+)]
+pub struct DailyUserActivityItem {
+    pub day: NaiveDate,
+    pub total_metadata_review_count: i64,
+    pub total_collection_review_count: i64,
+    pub total_metadata_group_review_count: i64,
+    pub total_person_review_count: i64,
+    pub user_measurement_count: i64,
+    pub workout_count: i64,
+    pub total_workout_duration: i64,
+    pub audio_book_count: i64,
+    pub total_audio_book_duration: i64,
+    pub anime_count: i64,
+    pub book_count: i64,
+    pub total_book_pages: i64,
+    pub podcast_count: i64,
+    pub total_podcast_duration: i64,
+    pub manga_count: i64,
+    pub movie_count: i64,
+    pub total_movie_duration: i64,
+    pub show_count: i64,
+    pub total_show_duration: i64,
+    pub video_game_count: i64,
+    pub total_video_game_duration: i64,
+    pub visual_novel_count: i64,
+    pub total_visual_novel_duration: i64,
+    pub total_workout_personal_bests: i64,
+    pub total_workout_weight: i64,
+    pub total_workout_reps: i64,
+    pub total_workout_distance: i64,
+    pub total_workout_rest_time: i64,
+    pub total_metadata_count: i64,
+    pub total_review_count: i64,
+    pub total_count: i64,
+    pub total_duration: i64,
+}
+
+#[skip_serializing_none]
+#[derive(Debug, Serialize, Deserialize, SimpleObject, Clone, PartialEq, Eq)]
+pub struct DailyUserActivitiesResponse {
+    pub total_count: i64,
+    pub item_count: usize,
+    pub total_duration: i64,
+    pub items: Vec<DailyUserActivityItem>,
+    pub grouped_by: DailyUserActivitiesResponseGroupedBy,
+}
+
+#[skip_serializing_none]
+#[derive(
+    Debug, SimpleObject, Serialize, Deserialize, FromJsonQueryResult, Clone, Eq, PartialEq,
+)]
+pub struct FitnessAnalyticsExercise {
+    pub count: u32,
+    pub exercise: String,
+}
+
+#[skip_serializing_none]
+#[derive(
+    Debug, SimpleObject, Serialize, Deserialize, FromJsonQueryResult, Clone, Eq, PartialEq,
+)]
+pub struct FitnessAnalyticsMuscle {
+    pub count: u32,
+    pub muscle: ExerciseMuscle,
+}
+
+#[skip_serializing_none]
+#[derive(
+    Debug, SimpleObject, Serialize, Deserialize, FromJsonQueryResult, Clone, Eq, PartialEq,
+)]
+pub struct FitnessAnalyticsEquipment {
+    pub count: u32,
+    pub equipment: ExerciseEquipment,
+}
+
+#[skip_serializing_none]
+#[derive(
+    Debug, SimpleObject, Serialize, Deserialize, FromJsonQueryResult, Clone, Eq, PartialEq,
+)]
+pub struct UserFitnessAnalytics {
+    pub workout_reps: i32,
+    pub workout_weight: i32,
+    pub workout_count: i32,
+    pub workout_distance: i32,
+    pub workout_duration: i32,
+    pub workout_rest_time: i32,
+    pub measurement_count: i32,
+    pub workout_personal_bests: i32,
+    pub workout_muscles: Vec<FitnessAnalyticsMuscle>,
+    pub workout_exercises: Vec<FitnessAnalyticsExercise>,
+    pub workout_equipments: Vec<FitnessAnalyticsEquipment>,
+}
+
+#[skip_serializing_none]
+#[derive(
+    Debug, SimpleObject, Serialize, Deserialize, FromJsonQueryResult, Clone, Eq, PartialEq,
+)]
+pub struct UserAnalytics {
+    pub fitness: UserFitnessAnalytics,
+    pub activities: DailyUserActivitiesResponse,
+    pub hours: Vec<DailyUserActivityHourRecord>,
+}
+
+#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
+pub struct TmdbLanguage {
+    pub iso_639_1: String,
+    pub english_name: String,
+}
+
+#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
+pub struct TmdbSettings {
+    pub image_url: String,
+    pub languages: Vec<TmdbLanguage>,
+}
+
+#[skip_serializing_none]
+#[derive(Clone, Debug, PartialEq, FromJsonQueryResult, Eq, Serialize, Deserialize)]
+pub enum ApplicationCacheKey {
+    IgdbSettings,
+    TmdbSettings,
+    ListennotesSettings,
+    ServerKeyValidated,
+    UserAnalyticsParameters {
+        user_id: String,
+    },
+    UserAnalytics {
+        user_id: String,
+        input: UserAnalyticsInput,
+    },
+    MetadataRecentlyConsumed {
+        user_id: String,
+        entity_id: String,
+        entity_lot: EntityLot,
+    },
+    ProgressUpdateCache {
+        user_id: String,
+        metadata_id: String,
+        show_season_number: Option<i32>,
+        show_episode_number: Option<i32>,
+        podcast_episode_number: Option<i32>,
+        anime_episode_number: Option<i32>,
+        manga_chapter_number: Option<Decimal>,
+        manga_volume_number: Option<i32>,
+    },
+}
+
+#[skip_serializing_none]
+#[derive(Clone, Debug, PartialEq, FromJsonQueryResult, Serialize, Deserialize, Eq)]
+pub enum ApplicationCacheValue {
+    Empty,
+    TmdbSettings(TmdbSettings),
+    UserAnalytics(UserAnalytics),
+    IgdbSettings { access_token: String },
+    UserAnalyticsParameters(ApplicationDateRange),
+    ListennotesSettings { genres: HashMap<i32, String> },
+}
diff --git a/crates/models/database/src/application_cache.rs b/crates/models/database/src/application_cache.rs
index a3d1249a59..f6cea0b5b1 100644
--- a/crates/models/database/src/application_cache.rs
+++ b/crates/models/database/src/application_cache.rs
@@ -1,6 +1,6 @@
 //! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.1
 
-use common_models::ApplicationCacheKey;
+use common_models::{ApplicationCacheKey, ApplicationCacheValue};
 use sea_orm::entity::prelude::*;
 
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
@@ -11,6 +11,8 @@ pub struct Model {
     pub created_at: DateTimeUtc,
     #[sea_orm(column_type = "Json")]
     pub key: ApplicationCacheKey,
+    #[sea_orm(column_type = "Json")]
+    pub value: ApplicationCacheValue,
     pub expires_at: Option<DateTimeUtc>,
 }
 
diff --git a/crates/models/database/src/daily_user_activity.rs b/crates/models/database/src/daily_user_activity.rs
index dd0916442a..ca16d3abe7 100644
--- a/crates/models/database/src/daily_user_activity.rs
+++ b/crates/models/database/src/daily_user_activity.rs
@@ -1,16 +1,17 @@
 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
 
 use common_models::DailyUserActivityHourRecord;
+use enums::{ExerciseEquipment, ExerciseMuscle};
 use sea_orm::entity::prelude::*;
 use serde::{Deserialize, Serialize};
 
 #[derive(Clone, Default, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[sea_orm(table_name = "daily_user_activity")]
 pub struct Model {
-    #[sea_orm(primary_key, auto_increment = false)]
+    #[sea_orm(primary_key)]
+    pub id: Uuid,
     pub user_id: String,
-    #[sea_orm(primary_key, auto_increment = false)]
-    pub date: Date,
+    pub date: Option<Date>,
     pub entity_ids: Vec<String>,
     pub metadata_review_count: i32,
     pub collection_review_count: i32,
@@ -47,6 +48,9 @@ pub struct Model {
     pub total_duration: i32,
     #[sea_orm(column_type = "Json")]
     pub hour_records: Vec<DailyUserActivityHourRecord>,
+    pub workout_exercises: Vec<String>,
+    pub workout_muscles: Vec<ExerciseMuscle>,
+    pub workout_equipments: Vec<ExerciseEquipment>,
 }
 
 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
diff --git a/crates/models/database/src/exercise.rs b/crates/models/database/src/exercise.rs
index 73451b0f0f..a91ca6f690 100644
--- a/crates/models/database/src/exercise.rs
+++ b/crates/models/database/src/exercise.rs
@@ -28,23 +28,21 @@ use serde::{Deserialize, Serialize};
 #[sea_orm(table_name = "exercise")]
 #[graphql(name = "Exercise", input_name = "ExerciseInput")]
 pub struct Model {
+    #[graphql(skip_input)]
     #[sea_orm(primary_key, auto_increment = false)]
     pub id: String,
-    #[graphql(skip)]
-    #[sea_orm(unique)]
-    pub identifier: Option<String>,
+    pub name: String,
     pub lot: ExerciseLot,
     pub level: ExerciseLevel,
-    pub force: Option<ExerciseForce>,
-    pub mechanic: Option<ExerciseMechanic>,
-    pub equipment: Option<ExerciseEquipment>,
     #[graphql(skip_input)]
     pub source: ExerciseSource,
-    #[sea_orm(column_type = "Json")]
+    pub force: Option<ExerciseForce>,
     pub muscles: Vec<ExerciseMuscle>,
     pub attributes: ExerciseAttributes,
     #[graphql(skip_input)]
     pub created_by_user_id: Option<String>,
+    pub mechanic: Option<ExerciseMechanic>,
+    pub equipment: Option<ExerciseEquipment>,
 }
 
 #[async_trait]
diff --git a/crates/models/database/src/functions.rs b/crates/models/database/src/functions.rs
index 34ba8454c4..4f4035887a 100644
--- a/crates/models/database/src/functions.rs
+++ b/crates/models/database/src/functions.rs
@@ -10,7 +10,7 @@ use crate::{prelude::UserToEntity, user_to_entity};
 pub async fn get_user_to_entity_association<C>(
     db: &C,
     user_id: &String,
-    entity_id: String,
+    entity_id: &String,
     entity_lot: EntityLot,
 ) -> Option<user_to_entity::Model>
 where
@@ -39,14 +39,14 @@ where
 pub async fn associate_user_with_entity<C>(
     db: &C,
     user_id: &String,
-    entity_id: String,
+    entity_id: &String,
     entity_lot: EntityLot,
 ) -> Result<user_to_entity::Model>
 where
     C: ConnectionTrait,
 {
-    let user_to_meta =
-        get_user_to_entity_association(db, user_id, entity_id.clone(), entity_lot).await;
+    let entity_id = entity_id.to_owned();
+    let user_to_meta = get_user_to_entity_association(db, user_id, &entity_id, entity_lot).await;
     Ok(match user_to_meta {
         None => {
             let mut user_to_meta = user_to_entity::ActiveModel {
diff --git a/crates/models/database/src/review.rs b/crates/models/database/src/review.rs
index 9c4a04e12e..0964ba387e 100644
--- a/crates/models/database/src/review.rs
+++ b/crates/models/database/src/review.rs
@@ -152,14 +152,9 @@ impl ActiveModelBehavior for ActiveModel {
         C: ConnectionTrait,
     {
         if insert {
-            associate_user_with_entity(
-                db,
-                &model.user_id,
-                model.entity_id.clone(),
-                model.entity_lot,
-            )
-            .await
-            .ok();
+            associate_user_with_entity(db, &model.user_id, &model.entity_id, model.entity_lot)
+                .await
+                .ok();
         }
         Ok(model)
     }
diff --git a/crates/models/database/src/seen.rs b/crates/models/database/src/seen.rs
index 7671948c24..bc3d893b70 100644
--- a/crates/models/database/src/seen.rs
+++ b/crates/models/database/src/seen.rs
@@ -118,14 +118,9 @@ impl ActiveModelBehavior for ActiveModel {
         C: ConnectionTrait,
     {
         if insert {
-            associate_user_with_entity(
-                db,
-                &model.user_id,
-                model.metadata_id.clone(),
-                EntityLot::Metadata,
-            )
-            .await
-            .ok();
+            associate_user_with_entity(db, &model.user_id, &model.metadata_id, EntityLot::Metadata)
+                .await
+                .ok();
         }
         Ok(model)
     }
diff --git a/crates/models/database/src/user.rs b/crates/models/database/src/user.rs
index 849ff4c422..5fdb2def6a 100644
--- a/crates/models/database/src/user.rs
+++ b/crates/models/database/src/user.rs
@@ -9,7 +9,7 @@ use async_trait::async_trait;
 use enums::UserLot;
 use sea_orm::{entity::prelude::*, ActiveValue};
 use serde::{Deserialize, Serialize};
-use user_models::UserPreferences;
+use user_models::{UserExtraInformation, UserPreferences};
 
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize, SimpleObject)]
 #[graphql(name = "User")]
@@ -28,7 +28,7 @@ pub struct Model {
     pub is_disabled: Option<bool>,
     pub preferences: UserPreferences,
     #[graphql(skip)]
-    pub extra_information: Option<Json>,
+    pub extra_information: Option<UserExtraInformation>,
 }
 
 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
diff --git a/crates/models/dependent/src/lib.rs b/crates/models/dependent/src/lib.rs
index b524d9840c..063f5e135c 100644
--- a/crates/models/dependent/src/lib.rs
+++ b/crates/models/dependent/src/lib.rs
@@ -12,11 +12,11 @@ use enums::{
 use fitness_models::{UserToExerciseHistoryExtraInformation, UserWorkoutInput};
 use importer_models::ImportFailedItem;
 use media_models::{
-    CreateOrUpdateCollectionInput, DailyUserActivitiesResponseGroupedBy, DailyUserActivityItem,
-    EntityWithLot, GenreListItem, GraphqlMediaAssets, ImportOrExportExerciseItem,
-    ImportOrExportMetadataGroupItem, ImportOrExportMetadataItem, ImportOrExportPersonItem,
-    MetadataCreatorGroupedByRole, PersonDetailsGroupedByRole, ReviewItem, UserDetailsError,
-    UserMediaNextEntry, UserMetadataDetailsEpisodeProgress, UserMetadataDetailsShowSeasonProgress,
+    CreateOrUpdateCollectionInput, EntityWithLot, GenreListItem, GraphqlMediaAssets,
+    ImportOrExportExerciseItem, ImportOrExportMetadataGroupItem, ImportOrExportMetadataItem,
+    ImportOrExportPersonItem, MetadataCreatorGroupedByRole, PersonDetailsGroupedByRole, ReviewItem,
+    UserDetailsError, UserMediaNextEntry, UserMetadataDetailsEpisodeProgress,
+    UserMetadataDetailsShowSeasonProgress,
 };
 use rust_decimal::Decimal;
 use schematic::Schematic;
@@ -102,7 +102,7 @@ pub struct UserExerciseDetails {
 
 #[derive(Clone, Debug, Deserialize, Serialize, InputObject)]
 pub struct UpdateCustomExerciseInput {
-    pub old_name: String,
+    pub old_id: String,
     pub should_delete: Option<bool>,
     #[graphql(flatten)]
     pub update: exercise::Model,
@@ -192,7 +192,6 @@ pub struct MetadataLotSourceMappings {
 
 #[derive(Debug, SimpleObject, Serialize, Deserialize)]
 pub struct CoreDetails {
-    pub is_pro: bool,
     pub page_size: i32,
     pub version: String,
     pub docs_link: String,
@@ -206,6 +205,7 @@ pub struct CoreDetails {
     pub token_valid_for_days: i32,
     pub local_auth_disabled: bool,
     pub file_storage_enabled: bool,
+    pub is_server_key_validated: bool,
     pub backend_errors: Vec<BackendError>,
     pub exercise_parameters: ExerciseParameters,
     pub metadata_lot_source_mappings: Vec<MetadataLotSourceMappings>,
@@ -214,42 +214,46 @@ pub struct CoreDetails {
 
 #[derive(SimpleObject)]
 pub struct UserPersonDetails {
+    pub recently_consumed: bool,
     pub reviews: Vec<ReviewItem>,
     pub collections: Vec<collection::Model>,
 }
 
 #[derive(SimpleObject)]
 pub struct UserMetadataGroupDetails {
+    pub recently_consumed: bool,
     pub reviews: Vec<ReviewItem>,
     pub collections: Vec<collection::Model>,
 }
 
 #[derive(SimpleObject)]
 pub struct UserMetadataDetails {
-    /// The reasons why this metadata is related to this user
-    pub media_reason: Option<Vec<UserToMediaReason>>,
-    /// The collections in which this media is present.
-    pub collections: Vec<collection::Model>,
+    /// Whether this media has been interacted with
+    pub has_interacted: bool,
+    /// Whether this media has been recently interacted with
+    pub recently_consumed: bool,
     /// The public reviews of this media.
     pub reviews: Vec<ReviewItem>,
-    /// The seen history of this media.
-    pub history: Vec<seen::Model>,
-    /// The seen item if it is in progress.
-    pub in_progress: Option<seen::Model>,
-    /// The next episode/chapter of this media.
-    pub next_entry: Option<UserMediaNextEntry>,
     /// The number of users who have seen this media.
     pub seen_by_all_count: usize,
     /// The number of times this user has seen this media.
     pub seen_by_user_count: usize,
+    /// The seen history of this media.
+    pub history: Vec<seen::Model>,
     /// The average rating of this media in this service.
     pub average_rating: Option<Decimal>,
+    /// The seen item if it is in progress.
+    pub in_progress: Option<seen::Model>,
+    /// The collections in which this media is present.
+    pub collections: Vec<collection::Model>,
+    /// The next episode/chapter of this media.
+    pub next_entry: Option<UserMediaNextEntry>,
+    /// The reasons why this metadata is related to this user
+    pub media_reason: Option<Vec<UserToMediaReason>>,
     /// The seen progress of this media if it is a show.
     pub show_progress: Option<Vec<UserMetadataDetailsShowSeasonProgress>>,
     /// The seen progress of this media if it is a podcast.
     pub podcast_progress: Option<Vec<UserMetadataDetailsEpisodeProgress>>,
-    /// Whether this media has been interacted with
-    pub has_interacted: bool,
 }
 
 #[derive(Debug, Default, Display, Clone, Serialize)]
@@ -272,15 +276,6 @@ pub struct ImportResult {
     pub completed: Vec<ImportCompletedItem>,
 }
 
-#[derive(Debug, Serialize, Deserialize, SimpleObject, Clone)]
-pub struct DailyUserActivitiesResponse {
-    pub total_count: i64,
-    pub item_count: usize,
-    pub total_duration: i64,
-    pub items: Vec<DailyUserActivityItem>,
-    pub grouped_by: DailyUserActivitiesResponseGroupedBy,
-}
-
 #[derive(Debug, SimpleObject, Clone, Serialize, Deserialize, Schematic)]
 pub struct UserWorkoutTemplateDetails {
     pub details: workout_template::Model,
diff --git a/crates/models/fitness/src/lib.rs b/crates/models/fitness/src/lib.rs
index c4c640aee9..67de880b19 100644
--- a/crates/models/fitness/src/lib.rs
+++ b/crates/models/fitness/src/lib.rs
@@ -71,8 +71,6 @@ pub struct GithubExerciseAttributes {
 #[derive(Debug, Clone, Serialize, Deserialize, FromJsonQueryResult, Eq, PartialEq)]
 #[serde(rename_all = "camelCase")]
 pub struct GithubExercise {
-    #[serde(alias = "id")]
-    pub identifier: String,
     #[serde(flatten)]
     pub attributes: GithubExerciseAttributes,
     pub name: String,
@@ -125,8 +123,9 @@ pub struct UserMeasurementStats {
 
 #[derive(Clone, Debug, Deserialize, SimpleObject, FromQueryResult)]
 pub struct ExerciseListItem {
-    pub lot: ExerciseLot,
     pub id: String,
+    pub name: String,
+    pub lot: ExerciseLot,
     #[graphql(skip)]
     pub attributes: ExerciseAttributes,
     pub num_times_interacted: Option<i32>,
@@ -293,6 +292,7 @@ pub struct SetRestTimersSettings {
     Debug, Clone, Serialize, Deserialize, FromJsonQueryResult, Eq, PartialEq, SimpleObject, Default,
 )]
 pub struct UserToExerciseSettingsExtraInformation {
+    pub exclude_from_analytics: bool,
     pub set_rest_timers: SetRestTimersSettings,
 }
 
@@ -344,7 +344,7 @@ pub struct EntityAssets {
 )]
 #[serde(rename_all = "snake_case")]
 pub struct ProcessedExercise {
-    pub name: String,
+    pub id: String,
     pub lot: ExerciseLot,
     pub notes: Vec<String>,
     pub sets: Vec<WorkoutSetRecord>,
@@ -430,7 +430,7 @@ pub struct WorkoutInformation {
 )]
 #[serde(rename_all = "snake_case")]
 pub struct WorkoutSummaryExercise {
-    pub name: String,
+    pub id: String,
     pub num_sets: usize,
     pub lot: Option<ExerciseLot>,
     pub best_set: Option<WorkoutSetRecord>,
diff --git a/crates/models/media/Cargo.toml b/crates/models/media/Cargo.toml
index 8aa5016a83..1c14fc2fd8 100644
--- a/crates/models/media/Cargo.toml
+++ b/crates/models/media/Cargo.toml
@@ -16,7 +16,6 @@ sea-orm = { workspace = true }
 serde = { workspace = true }
 serde_json = { workspace = true }
 serde_with = { workspace = true }
-strum = { workspace = true }
 
 [package.metadata.cargo-machete]
 ignored = ["serde_json"]
diff --git a/crates/models/media/src/lib.rs b/crates/models/media/src/lib.rs
index c6242ebc6c..1455cd1e86 100644
--- a/crates/models/media/src/lib.rs
+++ b/crates/models/media/src/lib.rs
@@ -9,17 +9,13 @@ use common_models::{
 use common_utils::deserialize_date;
 use enums::{
     EntityLot, ImportSource, IntegrationProvider, MediaLot, MediaSource, NotificationPlatformLot,
-    SeenState, UserLot, Visibility,
+    SeenState, Visibility,
 };
 use rust_decimal::Decimal;
 use schematic::Schematic;
-use sea_orm::{
-    prelude::{Date, DateTimeUtc},
-    EnumIter, FromJsonQueryResult, FromQueryResult,
-};
+use sea_orm::{prelude::DateTimeUtc, EnumIter, FromJsonQueryResult, FromQueryResult};
 use serde::{Deserialize, Serialize};
 use serde_with::skip_serializing_none;
-use strum::Display;
 
 #[derive(Debug, SimpleObject, Serialize, Deserialize, Clone)]
 pub struct EntityWithLot {
@@ -854,7 +850,10 @@ pub struct PersonStateChanges {
 #[graphql(input_name = "IntegrationSourceSpecificsInput")]
 #[serde(rename_all = "snake_case")]
 pub struct IntegrationProviderSpecifics {
-    pub plex_username: Option<String>,
+    pub plex_yank_base_url: Option<String>,
+    pub plex_yank_token: Option<String>,
+
+    pub plex_sink_username: Option<String>,
 
     pub audiobookshelf_base_url: Option<String>,
     pub audiobookshelf_token: Option<String>,
@@ -1116,18 +1115,6 @@ pub enum LoginResult {
     Error(LoginError),
 }
 
-#[derive(Debug, InputObject)]
-pub struct UpdateUserInput {
-    pub user_id: String,
-    pub is_disabled: Option<bool>,
-    pub lot: Option<UserLot>,
-    #[graphql(secret)]
-    pub password: Option<String>,
-    pub username: Option<String>,
-    pub extra_information: Option<serde_json::Value>,
-    pub admin_access_token: Option<String>,
-}
-
 #[derive(Debug, InputObject)]
 pub struct GenreDetailsInput {
     pub genre_id: String,
@@ -1494,56 +1481,3 @@ pub enum ProcessAccessLinkResult {
     Ok(ProcessAccessLinkResponse),
     Error(ProcessAccessLinkError),
 }
-
-#[derive(Debug, Serialize, Deserialize, Enum, Clone, Copy, Eq, PartialEq, Display)]
-#[strum(serialize_all = "snake_case")]
-pub enum DailyUserActivitiesResponseGroupedBy {
-    Day,
-    Month,
-    Year,
-    Millennium,
-}
-
-#[derive(Debug, Default, Serialize, Deserialize, InputObject, Clone)]
-pub struct DailyUserActivitiesInput {
-    pub end_date: Option<NaiveDate>,
-    pub start_date: Option<NaiveDate>,
-    pub group_by: Option<DailyUserActivitiesResponseGroupedBy>,
-}
-
-#[derive(Debug, Default, SimpleObject, Serialize, Deserialize, Clone, FromQueryResult)]
-pub struct DailyUserActivityItem {
-    pub day: Date,
-    pub total_metadata_review_count: i64,
-    pub total_collection_review_count: i64,
-    pub total_metadata_group_review_count: i64,
-    pub total_person_review_count: i64,
-    pub measurement_count: i64,
-    pub workout_count: i64,
-    pub total_workout_duration: i64,
-    pub audio_book_count: i64,
-    pub total_audio_book_duration: i64,
-    pub anime_count: i64,
-    pub book_count: i64,
-    pub total_book_pages: i64,
-    pub podcast_count: i64,
-    pub total_podcast_duration: i64,
-    pub manga_count: i64,
-    pub movie_count: i64,
-    pub total_movie_duration: i64,
-    pub show_count: i64,
-    pub total_show_duration: i64,
-    pub video_game_count: i64,
-    pub total_video_game_duration: i64,
-    pub visual_novel_count: i64,
-    pub total_visual_novel_duration: i64,
-    pub total_workout_personal_bests: i64,
-    pub total_workout_weight: i64,
-    pub total_workout_reps: i64,
-    pub total_workout_distance: i64,
-    pub total_workout_rest_time: i64,
-    pub total_metadata_count: i64,
-    pub total_review_count: i64,
-    pub total_count: i64,
-    pub total_duration: i64,
-}
diff --git a/crates/models/specific/Cargo.toml b/crates/models/specific/Cargo.toml
index 38cdfcaa7f..c8ec8b1250 100644
--- a/crates/models/specific/Cargo.toml
+++ b/crates/models/specific/Cargo.toml
@@ -4,6 +4,9 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
+common-models = { path = "../common" }
 rust_decimal = { workspace = true }
+sea-orm = { workspace = true }
 serde = { workspace = true }
+serde_with = { workspace = true }
 strum = { workspace = true }
diff --git a/crates/models/specific/src/lib.rs b/crates/models/specific/src/lib.rs
index f608f3f3d3..d917d35928 100644
--- a/crates/models/specific/src/lib.rs
+++ b/crates/models/specific/src/lib.rs
@@ -1,5 +1,8 @@
+use common_models::StringIdObject;
 use rust_decimal::Decimal;
+use sea_orm::prelude::DateTimeUtc;
 use serde::{Deserialize, Serialize};
+use serde_with::{formats::Flexible, serde_as, TimestampSeconds};
 use strum::Display;
 
 pub mod audiobookshelf {
@@ -74,3 +77,44 @@ pub mod audiobookshelf {
         pub results: Vec<Item>,
     }
 }
+
+pub mod plex {
+    use super::*;
+
+    #[serde_as]
+    #[derive(Debug, Deserialize, Serialize)]
+    pub struct PlexMetadataItem {
+        pub title: String,
+        #[serde(rename = "type")]
+        pub item_type: String,
+        #[serde(rename = "ratingKey")]
+        pub rating_key: Option<String>,
+        pub key: String,
+        #[serde(rename = "Guid")]
+        pub guid: Option<Vec<StringIdObject>>,
+        #[serde_as(as = "Option<TimestampSeconds<i64, Flexible>>")]
+        #[serde(rename = "lastViewedAt")]
+        pub last_viewed_at: Option<DateTimeUtc>,
+        pub index: Option<i32>,
+        #[serde(rename = "parentIndex")]
+        pub parent_index: Option<i32>,
+    }
+
+    #[derive(Debug, Deserialize, Serialize)]
+    #[serde(rename_all = "PascalCase")]
+    pub struct PlexLibrary {
+        pub directory: Vec<PlexMetadataItem>,
+    }
+
+    #[derive(Debug, Deserialize, Serialize)]
+    #[serde(rename_all = "PascalCase")]
+    pub struct PlexMetadata {
+        pub metadata: Vec<PlexMetadataItem>,
+    }
+
+    #[derive(Debug, Deserialize, Serialize)]
+    #[serde(rename_all = "PascalCase")]
+    pub struct PlexMediaResponse<T> {
+        pub media_container: T,
+    }
+}
diff --git a/crates/models/user/src/lib.rs b/crates/models/user/src/lib.rs
index 522fa37d96..54415accba 100644
--- a/crates/models/user/src/lib.rs
+++ b/crates/models/user/src/lib.rs
@@ -1,7 +1,7 @@
-use async_graphql::{Enum, SimpleObject};
+use async_graphql::{Enum, InputObject, SimpleObject};
 use common_models::MediaStateChanged;
 use educe::Educe;
-use enums::MediaLot;
+use enums::{MediaLot, UserLot};
 use fitness_models::{SetRestTimersSettings, UserUnitSystem};
 use sea_orm::{FromJsonQueryResult, Iterable};
 use serde::{Deserialize, Serialize};
@@ -180,18 +180,29 @@ pub struct UserFitnessMeasurementsPreferences {
     pub inbuilt: UserMeasurementsInBuiltPreferences,
 }
 
+#[derive(
+    Debug, Serialize, Deserialize, SimpleObject, Clone, Eq, PartialEq, FromJsonQueryResult, Educe,
+)]
+#[educe(Default)]
+pub struct UserAnalyticsFeaturesEnabledPreferences {
+    #[educe(Default = true)]
+    pub enabled: bool,
+}
+
 #[derive(
     Debug, Serialize, Deserialize, SimpleObject, Clone, Eq, PartialEq, Default, FromJsonQueryResult,
 )]
 pub struct UserFeaturesEnabledPreferences {
     pub media: UserMediaFeaturesEnabledPreferences,
-    pub fitness: UserFitnessFeaturesEnabledPreferences,
     pub others: UserOthersFeaturesEnabledPreferences,
+    pub fitness: UserFitnessFeaturesEnabledPreferences,
+    pub analytics: UserAnalyticsFeaturesEnabledPreferences,
 }
 
 #[derive(
-    Debug, Serialize, Deserialize, SimpleObject, Clone, Eq, PartialEq, Default, FromJsonQueryResult,
+    Eq, Clone, Debug, Educe, Serialize, PartialEq, Deserialize, SimpleObject, FromJsonQueryResult,
 )]
+#[educe(Default)]
 pub struct UserFitnessPreferences {
     pub logging: UserFitnessLoggingPreferences,
     pub exercises: UserFitnessExercisesPreferences,
@@ -249,7 +260,6 @@ pub enum DashboardElementLot {
     #[default]
     Summary,
     Recommendations,
-    Activity,
 }
 
 #[skip_serializing_none]
@@ -326,10 +336,6 @@ pub struct UserGeneralPreferences {
             section: DashboardElementLot::Recommendations,
             ..Default::default()
         },
-        UserGeneralDashboardElement {
-            section: DashboardElementLot::Activity,
-            ..Default::default()
-        },
     ]))]
     pub dashboard: Vec<UserGeneralDashboardElement>,
 }
@@ -383,3 +389,21 @@ pub enum NotificationPlatformSpecifics {
         chat_id: String,
     },
 }
+
+#[derive(
+    Debug, Serialize, Deserialize, Clone, Eq, PartialEq, FromJsonQueryResult, InputObject, Default,
+)]
+pub struct UserExtraInformation {
+    pub scheduled_for_workout_revision: bool,
+}
+
+#[derive(Debug, InputObject)]
+pub struct UpdateUserInput {
+    pub user_id: String,
+    pub lot: Option<UserLot>,
+    #[graphql(secret)]
+    pub password: Option<String>,
+    pub username: Option<String>,
+    pub is_disabled: Option<bool>,
+    pub admin_access_token: Option<String>,
+}
diff --git a/crates/providers/Cargo.toml b/crates/providers/Cargo.toml
index 2ff63807f4..8ce8c69875 100644
--- a/crates/providers/Cargo.toml
+++ b/crates/providers/Cargo.toml
@@ -8,7 +8,6 @@ anyhow = { workspace = true }
 application-utils = { path = "../utils/application" }
 async-trait = { workspace = true }
 chrono = { workspace = true }
-chrono-tz = { workspace = true }
 common-models = { path = "../models/common" }
 common-utils = { path = "../utils/common" }
 config = { path = "../config" }
@@ -35,6 +34,7 @@ serde = { workspace = true }
 serde_json = { workspace = true }
 serde_with = { workspace = true }
 strum = { workspace = true }
+supporting-service = { path = "../services/supporting" }
 tracing = { workspace = true }
 traits = { path = "../traits" }
 
diff --git a/crates/providers/src/igdb.rs b/crates/providers/src/igdb.rs
index f13a2e4800..50560ec15b 100644
--- a/crates/providers/src/igdb.rs
+++ b/crates/providers/src/igdb.rs
@@ -1,11 +1,13 @@
-use std::{collections::HashMap, fs, path::PathBuf};
+use std::{collections::HashMap, sync::Arc};
 
 use anyhow::{anyhow, Result};
 use application_utils::get_base_http_client;
 use async_trait::async_trait;
 use chrono::Datelike;
-use common_models::{IdObject, NamedObject, SearchDetails, StoredUrl};
-use common_utils::{ryot_log, PAGE_SIZE, TEMP_DIR};
+use common_models::{
+    ApplicationCacheKey, ApplicationCacheValue, IdObject, NamedObject, SearchDetails, StoredUrl,
+};
+use common_utils::{ryot_log, PAGE_SIZE};
 use database_models::metadata_group::MetadataGroupWithoutId;
 use dependent_models::SearchResults;
 use enums::{MediaLot, MediaSource};
@@ -26,17 +28,12 @@ use sea_orm::prelude::DateTimeUtc;
 use serde::{Deserialize, Serialize};
 use serde_json::{json, Value};
 use serde_with::{formats::Flexible, serde_as, TimestampSeconds};
+use supporting_service::SupportingService;
 use traits::{MediaProvider, MediaProviderLanguages};
 
 static URL: &str = "https://api.igdb.com/v4";
 static IMAGE_URL: &str = "https://images.igdb.com/igdb/image/upload";
 static AUTH_URL: &str = "https://id.twitch.tv/oauth2/token";
-static FILE: &str = "igdb.json";
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-struct Settings {
-    access_token: String,
-}
 
 static GAME_FIELDS: &str = "
 fields
@@ -157,11 +154,11 @@ struct IgdbItemResponse {
     rest_data: Option<HashMap<String, Value>>,
 }
 
-#[derive(Debug, Clone)]
+#[derive(Clone)]
 pub struct IgdbService {
     image_url: String,
     image_size: String,
-    config: config::VideoGameConfig,
+    supporting_service: Arc<SupportingService>,
 }
 
 impl MediaProviderLanguages for IgdbService {
@@ -175,11 +172,12 @@ impl MediaProviderLanguages for IgdbService {
 }
 
 impl IgdbService {
-    pub async fn new(config: &config::VideoGameConfig) -> Self {
+    pub async fn new(ss: Arc<SupportingService>) -> Self {
+        let config = ss.config.video_games.clone();
         Self {
+            supporting_service: ss,
             image_url: IMAGE_URL.to_owned(),
             image_size: config.igdb.image_size.to_string(),
-            config: config.clone(),
         }
     }
 }
@@ -193,7 +191,7 @@ impl MediaProvider for IgdbService {
         page: Option<i32>,
         _display_nsfw: bool,
     ) -> Result<SearchResults<MetadataGroupSearchItem>> {
-        let client = self.get_client().await;
+        let client = self.get_client_config().await?;
         let req_body = format!(
             r#"
 {fields}
@@ -235,7 +233,7 @@ offset: {offset};
         &self,
         identifier: &str,
     ) -> Result<(MetadataGroupWithoutId, Vec<PartialMetadataWithoutId>)> {
-        let client = self.get_client().await;
+        let client = self.get_client_config().await?;
         let req_body = format!(
             r"
 {fields}
@@ -296,7 +294,7 @@ where id = {id};
         _source_specifics: &Option<PersonSourceSpecifics>,
         _display_nsfw: bool,
     ) -> Result<SearchResults<PeopleSearchItem>> {
-        let client = self.get_client().await;
+        let client = self.get_client_config().await?;
         let req_body = format!(
             r#"
 {fields}
@@ -342,7 +340,7 @@ offset: {offset};
         identity: &str,
         _source_specifics: &Option<PersonSourceSpecifics>,
     ) -> Result<MetadataPerson> {
-        let client = self.get_client().await;
+        let client = self.get_client_config().await?;
         let req_body = format!(
             r#"
 {fields}
@@ -423,7 +421,7 @@ where id = {id};
     }
 
     async fn metadata_details(&self, identifier: &str) -> Result<MetadataDetails> {
-        let client = self.get_client().await;
+        let client = self.get_client_config().await?;
         let req_body = format!(
             r#"{field} where id = {id};"#,
             field = GAME_FIELDS,
@@ -438,7 +436,7 @@ where id = {id};
             .map_err(|e| anyhow!(e))?;
         ryot_log!(debug, "Response = {:?}", rsp);
         let mut details: Vec<IgdbItemResponse> = rsp.json().await.map_err(|e| anyhow!(e))?;
-        let detail = details.pop().unwrap();
+        let detail = details.pop().ok_or_else(|| anyhow!("No details found"))?;
         let groups = match detail.collection.as_ref() {
             Some(c) => vec![c.id.to_string()],
             None => vec![],
@@ -455,7 +453,7 @@ where id = {id};
         _display_nsfw: bool,
     ) -> Result<SearchResults<MetadataSearchItem>> {
         let page = page.unwrap_or(1);
-        let client = self.get_client().await;
+        let client = self.get_client_config().await?;
         let count_req_body =
             format!(r#"fields id; where version_parent = null; search "{query}"; limit: 500;"#);
         let rsp = client
@@ -524,9 +522,9 @@ impl IgdbService {
         let access_res = client
             .post(AUTH_URL)
             .query(&json!({
-                "client_id": self.config.twitch.client_id.to_owned(),
-                "client_secret": self.config.twitch.client_secret.to_owned(),
                 "grant_type": "client_credentials".to_owned(),
+                "client_id": self.supporting_service.config.video_games.twitch.client_id.to_owned(),
+                "client_secret": self.supporting_service.config.video_games.twitch.client_secret.to_owned(),
             }))
             .send()
             .await
@@ -538,28 +536,34 @@ impl IgdbService {
         format!("{} {}", access.token_type, access.access_token)
     }
 
-    async fn get_client(&self) -> Client {
-        let path = PathBuf::new().join(TEMP_DIR).join(FILE);
-        let settings = if !path.exists() {
-            let tok = self.get_access_token().await;
-            let settings = Settings { access_token: tok };
-            let data_to_write = serde_json::to_string(&settings).unwrap();
-            fs::write(path, data_to_write).unwrap();
-            settings
+    async fn get_client_config(&self) -> Result<Client> {
+        let cc = &self.supporting_service.cache_service;
+        let maybe_settings = cc.get_key(ApplicationCacheKey::IgdbSettings).await.ok();
+        let access_token = if let Some(Some(ApplicationCacheValue::IgdbSettings { access_token })) =
+            maybe_settings
+        {
+            access_token
         } else {
-            let data = fs::read_to_string(path).unwrap();
-            serde_json::from_str(&data).unwrap()
+            let access_token = self.get_access_token().await;
+            cc.set_with_expiry(
+                ApplicationCacheKey::IgdbSettings,
+                None,
+                ApplicationCacheValue::IgdbSettings {
+                    access_token: access_token.clone(),
+                },
+            )
+            .await
+            .ok();
+            access_token
         };
-        get_base_http_client(Some(vec![
+        Ok(get_base_http_client(Some(vec![
             (
                 HeaderName::from_static("client-id"),
-                HeaderValue::from_str(&self.config.twitch.client_id).unwrap(),
-            ),
-            (
-                AUTHORIZATION,
-                HeaderValue::from_str(&settings.access_token).unwrap(),
+                HeaderValue::from_str(&self.supporting_service.config.video_games.twitch.client_id)
+                    .unwrap(),
             ),
-        ]))
+            (AUTHORIZATION, HeaderValue::from_str(&access_token).unwrap()),
+        ])))
     }
 
     fn igdb_response_to_search_response(&self, item: IgdbItemResponse) -> MetadataDetails {
diff --git a/crates/providers/src/listennotes.rs b/crates/providers/src/listennotes.rs
index 29faef33a9..80aca4e33d 100644
--- a/crates/providers/src/listennotes.rs
+++ b/crates/providers/src/listennotes.rs
@@ -1,11 +1,11 @@
-use std::{collections::HashMap, env, fs, path::PathBuf};
+use std::{collections::HashMap, env, sync::Arc};
 
 use anyhow::{anyhow, Result};
 use application_utils::get_base_http_client;
 use async_trait::async_trait;
 use chrono::Datelike;
-use common_models::SearchDetails;
-use common_utils::{convert_naive_to_utc, PAGE_SIZE, TEMP_DIR};
+use common_models::{ApplicationCacheKey, ApplicationCacheValue, SearchDetails};
+use common_utils::{convert_naive_to_utc, PAGE_SIZE};
 use dependent_models::SearchResults;
 use enums::{MediaLot, MediaSource};
 use itertools::Itertools;
@@ -22,21 +22,15 @@ use sea_orm::prelude::DateTimeUtc;
 use serde::{Deserialize, Serialize};
 use serde_json::json;
 use serde_with::{formats::Flexible, serde_as, TimestampMilliSeconds};
+use supporting_service::SupportingService;
 use traits::{MediaProvider, MediaProviderLanguages};
 
 static URL: &str = "https://listen-api.listennotes.com/api/v2";
-static FILE: &str = "listennotes.json";
 
-#[derive(Debug, Serialize, Deserialize, Clone)]
-struct Settings {
-    genres: HashMap<i32, String>,
-}
-
-#[derive(Debug, Clone)]
 pub struct ListennotesService {
     url: String,
     client: Client,
-    settings: Settings,
+    supporting_service: Arc<SupportingService>,
 }
 
 impl MediaProviderLanguages for ListennotesService {
@@ -50,16 +44,19 @@ impl MediaProviderLanguages for ListennotesService {
 }
 
 impl ListennotesService {
-    pub async fn new(config: &config::PodcastConfig) -> Self {
+    pub async fn new(ss: Arc<SupportingService>) -> Self {
         let url = env::var("LISTENNOTES_API_URL")
             .unwrap_or_else(|_| URL.to_owned())
             .as_str()
             .to_owned();
-        let (client, settings) = get_client_config(&config.listennotes.api_token).await;
+        let client = get_base_http_client(Some(vec![(
+            HeaderName::from_static("x-listenapi-key"),
+            HeaderValue::from_str(&ss.config.podcasts.listennotes.api_token).unwrap(),
+        )]));
         Self {
             url,
             client,
-            settings,
+            supporting_service: ss,
         }
     }
 }
@@ -152,13 +149,14 @@ impl MediaProvider for ListennotesService {
             results: Vec<Podcast>,
             next_offset: Option<i32>,
         }
+
         let rsp = self
             .client
             .get(format!("{}/search", self.url))
             .query(&json!({
+                "type": "podcast",
                 "q": query.to_owned(),
                 "offset": (page - 1) * PAGE_SIZE,
-                "type": "podcast"
             }))
             .send()
             .await
@@ -186,6 +184,52 @@ impl MediaProvider for ListennotesService {
 }
 
 impl ListennotesService {
+    async fn get_genres(&self) -> Result<HashMap<i32, String>> {
+        let cc = &self.supporting_service.cache_service;
+        let maybe_settings = cc
+            .get_key(ApplicationCacheKey::ListennotesSettings)
+            .await
+            .ok();
+        let genres = if let Some(Some(ApplicationCacheValue::ListennotesSettings { genres })) =
+            maybe_settings
+        {
+            genres
+        } else {
+            #[derive(Debug, Serialize, Deserialize, Default)]
+            #[serde(rename_all = "snake_case")]
+            pub struct ListennotesIdAndNamedObject {
+                pub id: i32,
+                pub name: String,
+            }
+            #[derive(Debug, Serialize, Deserialize, Default)]
+            struct GenreResponse {
+                genres: Vec<ListennotesIdAndNamedObject>,
+            }
+            let rsp = self
+                .client
+                .get(format!("{}/genres", self.url))
+                .send()
+                .await
+                .unwrap();
+            let data: GenreResponse = rsp.json().await.unwrap_or_default();
+            let mut genres = HashMap::new();
+            for genre in data.genres {
+                genres.insert(genre.id, genre.name);
+            }
+            cc.set_with_expiry(
+                ApplicationCacheKey::ListennotesSettings,
+                None,
+                ApplicationCacheValue::ListennotesSettings {
+                    genres: genres.clone(),
+                },
+            )
+            .await
+            .ok();
+            genres
+        };
+        Ok(genres)
+    }
+
     // The API does not return all the episodes for a podcast, and instead needs to be
     // paginated through. It also does not return the episode number. So we have to
     // handle those manually.
@@ -212,7 +256,7 @@ impl ListennotesService {
             genre_ids: Vec<i32>,
             total_episodes: usize,
         }
-        let  rsp = self
+        let resp = self
             .client
             .get(format!("{}/podcasts/{}", self.url, identifier))
             .query(&json!({
@@ -222,7 +266,8 @@ impl ListennotesService {
             .send()
             .await
             .map_err(|e| anyhow!(e))?;
-        let podcast_data: Podcast = rsp.json().await.map_err(|e| anyhow!(e))?;
+        let podcast_data: Podcast = resp.json().await.map_err(|e| anyhow!(e))?;
+        let genres = self.get_genres().await?;
         Ok(MetadataDetails {
             identifier: podcast_data.id,
             title: podcast_data.title,
@@ -238,7 +283,7 @@ impl ListennotesService {
             genres: podcast_data
                 .genre_ids
                 .into_iter()
-                .filter_map(|g| self.settings.genres.get(&g).cloned())
+                .filter_map(|g| genres.get(&g).cloned())
                 .unique()
                 .collect(),
             url_images: Vec::from_iter(
@@ -266,37 +311,3 @@ impl ListennotesService {
         })
     }
 }
-
-async fn get_client_config(api_token: &str) -> (Client, Settings) {
-    let client = get_base_http_client(Some(vec![(
-        HeaderName::from_static("x-listenapi-key"),
-        HeaderValue::from_str(api_token).unwrap(),
-    )]));
-    let path = PathBuf::new().join(TEMP_DIR).join(FILE);
-    let settings = if !path.exists() {
-        #[derive(Debug, Serialize, Deserialize, Default)]
-        #[serde(rename_all = "snake_case")]
-        pub struct ListennotesIdAndNamedObject {
-            pub id: i32,
-            pub name: String,
-        }
-        #[derive(Debug, Serialize, Deserialize, Default)]
-        struct GenreResponse {
-            genres: Vec<ListennotesIdAndNamedObject>,
-        }
-        let rsp = client.get(format!("{}/genres", URL)).send().await.unwrap();
-        let data: GenreResponse = rsp.json().await.unwrap_or_default();
-        let mut genres = HashMap::new();
-        for genre in data.genres {
-            genres.insert(genre.id, genre.name);
-        }
-        let settings = Settings { genres };
-        let data_to_write = serde_json::to_string(&settings);
-        fs::write(path, data_to_write.unwrap()).unwrap();
-        settings
-    } else {
-        let data = fs::read_to_string(path).unwrap();
-        serde_json::from_str(&data).unwrap()
-    };
-    (client, settings)
-}
diff --git a/crates/providers/src/tmdb.rs b/crates/providers/src/tmdb.rs
index 3f24ffd8fa..02f00a819c 100644
--- a/crates/providers/src/tmdb.rs
+++ b/crates/providers/src/tmdb.rs
@@ -1,7 +1,5 @@
 use std::{
     collections::{HashMap, HashSet},
-    fs,
-    path::PathBuf,
     sync::Arc,
 };
 
@@ -9,10 +7,11 @@ use anyhow::{anyhow, Result};
 use application_utils::{get_base_http_client, get_current_date};
 use async_trait::async_trait;
 use chrono::NaiveDate;
-use common_models::{IdObject, NamedObject, SearchDetails, StoredUrl};
-use common_utils::{
-    convert_date_to_year, convert_string_to_date, SHOW_SPECIAL_SEASON_NAMES, TEMP_DIR,
+use common_models::{
+    ApplicationCacheKey, ApplicationCacheValue, IdObject, NamedObject, SearchDetails, StoredUrl,
+    TmdbLanguage, TmdbSettings,
 };
+use common_utils::{convert_date_to_year, convert_string_to_date, SHOW_SPECIAL_SEASON_NAMES};
 use database_models::metadata_group::MetadataGroupWithoutId;
 use dependent_models::SearchResults;
 use enums::{MediaLot, MediaSource};
@@ -34,22 +33,10 @@ use rust_decimal_macros::dec;
 use sea_orm::prelude::DateTimeUtc;
 use serde::{Deserialize, Serialize};
 use serde_json::json;
+use supporting_service::SupportingService;
 use traits::{MediaProvider, MediaProviderLanguages};
 
 static URL: &str = "https://api.themoviedb.org/3";
-static FILE: &str = "tmdb.json";
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-struct Settings {
-    image_url: String,
-    languages: Vec<TmdbLanguage>,
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-struct TmdbLanguage {
-    iso_639_1: String,
-    english_name: String,
-}
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
 struct TmdbCredit {
@@ -179,12 +166,28 @@ struct TmdbNonMediaEntity {
     place_of_birth: Option<String>,
 }
 
-#[derive(Debug, Clone)]
 pub struct TmdbService {
     client: Client,
     language: String,
-    settings: Settings,
-    timezone: Arc<chrono_tz::Tz>,
+    settings: TmdbSettings,
+    supporting_service: Arc<SupportingService>,
+}
+
+impl TmdbService {
+    pub async fn new(ss: Arc<SupportingService>) -> Self {
+        let access_token = &ss.config.movies_and_shows.tmdb.access_token;
+        let client: Client = get_base_http_client(Some(vec![(
+            AUTHORIZATION,
+            HeaderValue::from_str(&format!("Bearer {access_token}")).unwrap(),
+        )]));
+        let settings = get_settings(&client, &ss).await.unwrap();
+        Self {
+            client,
+            settings,
+            language: ss.config.movies_and_shows.tmdb.locale.clone(),
+            supporting_service: ss,
+        }
+    }
 }
 
 impl TmdbService {
@@ -351,7 +354,7 @@ impl TmdbService {
         struct TmdbChangesResponse {
             changes: Vec<serde_json::Value>,
         }
-        let end_date = get_current_date(&self.timezone);
+        let end_date = get_current_date(&self.supporting_service.timezone);
         let start_date = since.date_naive();
         let changes = self
             .client
@@ -396,21 +399,14 @@ impl MediaProviderLanguages for TmdbService {
     }
 }
 
-#[derive(Debug, Clone)]
 pub struct NonMediaTmdbService {
     base: TmdbService,
 }
 
 impl NonMediaTmdbService {
-    pub async fn new(access_token: &str, language: String, timezone: Arc<chrono_tz::Tz>) -> Self {
-        let (client, settings) = get_client_config(access_token).await;
+    pub async fn new(ss: Arc<SupportingService>) -> Self {
         Self {
-            base: TmdbService {
-                client,
-                language,
-                settings,
-                timezone,
-            },
+            base: TmdbService::new(ss).await,
         }
     }
 }
@@ -424,6 +420,13 @@ impl MediaProvider for NonMediaTmdbService {
         source_specifics: &Option<PersonSourceSpecifics>,
         display_nsfw: bool,
     ) -> Result<SearchResults<PeopleSearchItem>> {
+        let language = &self
+            .base
+            .supporting_service
+            .config
+            .movies_and_shows
+            .tmdb
+            .locale;
         let type_ = match source_specifics {
             Some(PersonSourceSpecifics {
                 is_tmdb_company: Some(true),
@@ -437,9 +440,9 @@ impl MediaProvider for NonMediaTmdbService {
             .client
             .get(format!("{}/search/{}", URL, type_))
             .query(&json!({
-                "query": query.to_owned(),
                 "page": page,
-                "language": self.base.language,
+                "language": language,
+                "query": query.to_owned(),
                 "include_adult": display_nsfw,
             }))
             .send()
@@ -623,21 +626,14 @@ impl NonMediaTmdbService {
     }
 }
 
-#[derive(Debug, Clone)]
 pub struct TmdbMovieService {
     base: TmdbService,
 }
 
 impl TmdbMovieService {
-    pub async fn new(config: &config::TmdbConfig, timezone: Arc<chrono_tz::Tz>) -> Self {
-        let (client, settings) = get_client_config(&config.access_token).await;
+    pub async fn new(ss: Arc<SupportingService>) -> Self {
         Self {
-            base: TmdbService {
-                client,
-                language: config.locale.clone(),
-                settings,
-                timezone,
-            },
+            base: TmdbService::new(ss).await,
         }
     }
 }
@@ -973,21 +969,14 @@ impl MediaProvider for TmdbMovieService {
     }
 }
 
-#[derive(Debug, Clone)]
 pub struct TmdbShowService {
     base: TmdbService,
 }
 
 impl TmdbShowService {
-    pub async fn new(config: &config::TmdbConfig, timezone: Arc<chrono_tz::Tz>) -> Self {
-        let (client, settings) = get_client_config(&config.access_token).await;
+    pub async fn new(ss: Arc<SupportingService>) -> Self {
         Self {
-            base: TmdbService {
-                client,
-                language: config.locale.clone(),
-                settings,
-                timezone,
-            },
+            base: TmdbService::new(ss).await,
         }
     }
 }
@@ -1315,47 +1304,6 @@ impl MediaProvider for TmdbShowService {
     }
 }
 
-async fn get_client_config(access_token: &str) -> (Client, Settings) {
-    let client: Client = get_base_http_client(Some(vec![(
-        AUTHORIZATION,
-        HeaderValue::from_str(&format!("Bearer {access_token}")).unwrap(),
-    )]));
-    let path = PathBuf::new().join(TEMP_DIR).join(FILE);
-    let tmdb_settings = if !path.exists() {
-        #[derive(Debug, Serialize, Deserialize, Clone)]
-        struct TmdbImageConfiguration {
-            secure_base_url: String,
-        }
-        #[derive(Debug, Serialize, Deserialize, Clone)]
-        struct TmdbConfiguration {
-            images: TmdbImageConfiguration,
-        }
-        let rsp = client
-            .get(format!("{}/configuration", URL))
-            .send()
-            .await
-            .unwrap();
-        let data_1: TmdbConfiguration = rsp.json().await.unwrap();
-        let rsp = client
-            .get(format!("{}/configuration/languages", URL))
-            .send()
-            .await
-            .unwrap();
-        let data_2: Vec<TmdbLanguage> = rsp.json().await.unwrap();
-        let tmdb_settings = Settings {
-            image_url: data_1.images.secure_base_url,
-            languages: data_2,
-        };
-        let data_to_write = serde_json::to_string(&tmdb_settings);
-        fs::write(path, data_to_write.unwrap()).unwrap();
-        tmdb_settings
-    } else {
-        let data = fs::read_to_string(path).unwrap();
-        serde_json::from_str(&data).unwrap()
-    };
-    (client, tmdb_settings)
-}
-
 fn replace_from_end(input_string: String, search_string: &str, replace_string: &str) -> String {
     if let Some(last_index) = input_string.rfind(search_string) {
         let mut modified_string = input_string.clone();
@@ -1365,3 +1313,44 @@ fn replace_from_end(input_string: String, search_string: &str, replace_string: &
     }
     input_string
 }
+
+async fn get_settings(
+    client: &Client,
+    supporting_service: &Arc<SupportingService>,
+) -> Result<TmdbSettings> {
+    let cc = &supporting_service.cache_service;
+    let maybe_settings = cc.get_key(ApplicationCacheKey::TmdbSettings).await.ok();
+    let tmdb_settings =
+        if let Some(Some(ApplicationCacheValue::TmdbSettings(setting))) = maybe_settings {
+            setting
+        } else {
+            #[derive(Debug, Serialize, Deserialize, Clone)]
+            struct TmdbImageConfiguration {
+                secure_base_url: String,
+            }
+            #[derive(Debug, Serialize, Deserialize, Clone)]
+            struct TmdbConfiguration {
+                images: TmdbImageConfiguration,
+            }
+            let rsp = client.get(format!("{}/configuration", URL)).send().await?;
+            let data_1: TmdbConfiguration = rsp.json().await?;
+            let rsp = client
+                .get(format!("{}/configuration/languages", URL))
+                .send()
+                .await?;
+            let data_2: Vec<TmdbLanguage> = rsp.json().await?;
+            let settings = TmdbSettings {
+                image_url: data_1.images.secure_base_url,
+                languages: data_2,
+            };
+            cc.set_with_expiry(
+                ApplicationCacheKey::TmdbSettings,
+                None,
+                ApplicationCacheValue::TmdbSettings(settings.clone()),
+            )
+            .await
+            .ok();
+            settings
+        };
+    Ok(tmdb_settings)
+}
diff --git a/crates/resolvers/fitness/src/lib.rs b/crates/resolvers/fitness/src/lib.rs
index d780c437a4..8fd86e2058 100644
--- a/crates/resolvers/fitness/src/lib.rs
+++ b/crates/resolvers/fitness/src/lib.rs
@@ -2,7 +2,7 @@ use std::sync::Arc;
 
 use async_graphql::{Context, Object, Result};
 use common_models::SearchInput;
-use database_models::{exercise, user_measurement, workout, workout_template};
+use database_models::{exercise, user_measurement};
 use dependent_models::{
     SearchResults, UpdateCustomExerciseInput, UserExerciseDetails, UserWorkoutDetails,
     UserWorkoutTemplateDetails,
@@ -11,24 +11,24 @@ use fitness_models::{
     ExerciseListItem, ExercisesListInput, UpdateUserExerciseSettings,
     UpdateUserWorkoutAttributesInput, UserMeasurementsListInput, UserWorkoutInput,
 };
-use fitness_service::ExerciseService;
+use fitness_service::FitnessService;
 use sea_orm::prelude::DateTimeUtc;
 use traits::AuthProvider;
 
 #[derive(Default)]
-pub struct ExerciseQuery;
+pub struct FitnessQuery;
 
-impl AuthProvider for ExerciseQuery {}
+impl AuthProvider for FitnessQuery {}
 
 #[Object]
-impl ExerciseQuery {
+impl FitnessQuery {
     /// Get a paginated list of templates created by the user.
     async fn user_workout_templates_list(
         &self,
         gql_ctx: &Context<'_>,
         input: SearchInput,
-    ) -> Result<SearchResults<workout_template::Model>> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+    ) -> Result<SearchResults<String>> {
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service.user_workout_templates_list(user_id, input).await
     }
@@ -39,7 +39,7 @@ impl ExerciseQuery {
         gql_ctx: &Context<'_>,
         workout_template_id: String,
     ) -> Result<UserWorkoutTemplateDetails> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service
             .user_workout_template_details(user_id, workout_template_id)
@@ -52,7 +52,7 @@ impl ExerciseQuery {
         gql_ctx: &Context<'_>,
         input: ExercisesListInput,
     ) -> Result<SearchResults<ExerciseListItem>> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service.exercises_list(user_id, input).await
     }
@@ -62,8 +62,8 @@ impl ExerciseQuery {
         &self,
         gql_ctx: &Context<'_>,
         input: SearchInput,
-    ) -> Result<SearchResults<workout::Model>> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+    ) -> Result<SearchResults<String>> {
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service.user_workouts_list(user_id, input).await
     }
@@ -74,7 +74,7 @@ impl ExerciseQuery {
         gql_ctx: &Context<'_>,
         exercise_id: String,
     ) -> Result<exercise::Model> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         service.exercise_details(exercise_id).await
     }
 
@@ -84,7 +84,7 @@ impl ExerciseQuery {
         gql_ctx: &Context<'_>,
         workout_id: String,
     ) -> Result<UserWorkoutDetails> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service.user_workout_details(&user_id, workout_id).await
     }
@@ -95,7 +95,7 @@ impl ExerciseQuery {
         gql_ctx: &Context<'_>,
         exercise_id: String,
     ) -> Result<UserExerciseDetails> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service.user_exercise_details(user_id, exercise_id).await
     }
@@ -106,30 +106,30 @@ impl ExerciseQuery {
         gql_ctx: &Context<'_>,
         input: UserMeasurementsListInput,
     ) -> Result<Vec<user_measurement::Model>> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service.user_measurements_list(&user_id, input).await
     }
 }
 
 #[derive(Default)]
-pub struct ExerciseMutation;
+pub struct FitnessMutation;
 
-impl AuthProvider for ExerciseMutation {
+impl AuthProvider for FitnessMutation {
     fn is_mutation(&self) -> bool {
         true
     }
 }
 
 #[Object]
-impl ExerciseMutation {
+impl FitnessMutation {
     /// Create or update a workout template.
     async fn create_or_update_user_workout_template(
         &self,
         gql_ctx: &Context<'_>,
         input: UserWorkoutInput,
     ) -> Result<String> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service
             .create_or_update_user_workout_template(user_id, input)
@@ -142,7 +142,7 @@ impl ExerciseMutation {
         gql_ctx: &Context<'_>,
         workout_template_id: String,
     ) -> Result<bool> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service
             .delete_user_workout_template(user_id, workout_template_id)
@@ -155,7 +155,7 @@ impl ExerciseMutation {
         gql_ctx: &Context<'_>,
         input: user_measurement::Model,
     ) -> Result<DateTimeUtc> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service.create_user_measurement(&user_id, input).await
     }
@@ -166,7 +166,7 @@ impl ExerciseMutation {
         gql_ctx: &Context<'_>,
         timestamp: DateTimeUtc,
     ) -> Result<bool> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service.delete_user_measurement(user_id, timestamp).await
     }
@@ -177,7 +177,7 @@ impl ExerciseMutation {
         gql_ctx: &Context<'_>,
         input: UserWorkoutInput,
     ) -> Result<String> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service.create_or_update_user_workout(&user_id, input).await
     }
@@ -188,14 +188,14 @@ impl ExerciseMutation {
         gql_ctx: &Context<'_>,
         input: UpdateUserWorkoutAttributesInput,
     ) -> Result<bool> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service.update_user_workout_attributes(user_id, input).await
     }
 
     /// Delete a workout and remove all exercise associations.
     async fn delete_user_workout(&self, gql_ctx: &Context<'_>, workout_id: String) -> Result<bool> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service.delete_user_workout(user_id, workout_id).await
     }
@@ -206,7 +206,7 @@ impl ExerciseMutation {
         gql_ctx: &Context<'_>,
         input: exercise::Model,
     ) -> Result<String> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service.create_custom_exercise(&user_id, input).await
     }
@@ -217,7 +217,7 @@ impl ExerciseMutation {
         gql_ctx: &Context<'_>,
         input: UpdateCustomExerciseInput,
     ) -> Result<bool> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service.update_custom_exercise(user_id, input).await
     }
@@ -228,7 +228,7 @@ impl ExerciseMutation {
         gql_ctx: &Context<'_>,
         input: UpdateUserExerciseSettings,
     ) -> Result<bool> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service.update_user_exercise_settings(user_id, input).await
     }
@@ -240,7 +240,7 @@ impl ExerciseMutation {
         merge_from: String,
         merge_into: String,
     ) -> Result<bool> {
-        let service = gql_ctx.data_unchecked::<Arc<ExerciseService>>();
+        let service = gql_ctx.data_unchecked::<Arc<FitnessService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
         service
             .merge_exercise(user_id, merge_from, merge_into)
diff --git a/crates/resolvers/statistics/Cargo.toml b/crates/resolvers/statistics/Cargo.toml
index 47d918a7fd..96a442d9c6 100644
--- a/crates/resolvers/statistics/Cargo.toml
+++ b/crates/resolvers/statistics/Cargo.toml
@@ -5,7 +5,6 @@ edition = "2021"
 
 [dependencies]
 async-graphql = { workspace = true }
-dependent-models = { path = "../../models/dependent" }
-media-models = { path = "../../models/media" }
+common-models = { path = "../../models/common" }
 statistics-service = { path = "../../services/statistics" }
 traits = { path = "../../traits" }
diff --git a/crates/resolvers/statistics/src/lib.rs b/crates/resolvers/statistics/src/lib.rs
index f3d2a94386..7505faf856 100644
--- a/crates/resolvers/statistics/src/lib.rs
+++ b/crates/resolvers/statistics/src/lib.rs
@@ -1,8 +1,7 @@
 use std::sync::Arc;
 
 use async_graphql::{Context, Object, Result};
-use dependent_models::DailyUserActivitiesResponse;
-use media_models::{DailyUserActivitiesInput, DailyUserActivityItem};
+use common_models::{ApplicationDateRange, UserAnalytics, UserAnalyticsInput};
 use statistics_service::StatisticsService;
 use traits::AuthProvider;
 
@@ -13,21 +12,24 @@ impl AuthProvider for StatisticsQuery {}
 
 #[Object]
 impl StatisticsQuery {
-    /// Get daily user activities for the currently logged in user.
-    async fn daily_user_activities(
+    /// Get the analytics parameters for the currently logged in user.
+    async fn user_analytics_parameters(
         &self,
         gql_ctx: &Context<'_>,
-        input: DailyUserActivitiesInput,
-    ) -> Result<DailyUserActivitiesResponse> {
+    ) -> Result<ApplicationDateRange> {
         let service = gql_ctx.data_unchecked::<Arc<StatisticsService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
-        service.daily_user_activities(&user_id, input).await
+        service.user_analytics_parameters(&user_id).await
     }
 
-    /// Get a summary of all the media items that have been consumed by this user.
-    async fn latest_user_summary(&self, gql_ctx: &Context<'_>) -> Result<DailyUserActivityItem> {
+    /// Get the analytics for the currently logged in user.
+    async fn user_analytics(
+        &self,
+        gql_ctx: &Context<'_>,
+        input: UserAnalyticsInput,
+    ) -> Result<UserAnalytics> {
         let service = gql_ctx.data_unchecked::<Arc<StatisticsService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
-        service.latest_user_summary(&user_id).await
+        service.user_analytics(&user_id, input).await
     }
 }
diff --git a/crates/resolvers/user/Cargo.toml b/crates/resolvers/user/Cargo.toml
index f7902d2a02..dfd79055d1 100644
--- a/crates/resolvers/user/Cargo.toml
+++ b/crates/resolvers/user/Cargo.toml
@@ -10,4 +10,5 @@ database-models = { path = "../../models/database" }
 dependent-models = { path = "../../models/dependent" }
 media-models = { path = "../../models/media" }
 traits = { path = "../../traits" }
+user-models = { path = "../../models/user" }
 user-service = { path = "../../services/user" }
diff --git a/crates/resolvers/user/src/lib.rs b/crates/resolvers/user/src/lib.rs
index d727d9191c..baddc93fb8 100644
--- a/crates/resolvers/user/src/lib.rs
+++ b/crates/resolvers/user/src/lib.rs
@@ -7,10 +7,11 @@ use dependent_models::UserDetailsResult;
 use media_models::{
     AuthUserInput, CreateAccessLinkInput, CreateUserIntegrationInput,
     CreateUserNotificationPlatformInput, LoginResult, OidcTokenOutput, ProcessAccessLinkInput,
-    ProcessAccessLinkResult, RegisterResult, RegisterUserInput, UpdateUserInput,
-    UpdateUserIntegrationInput, UpdateUserNotificationPlatformInput,
+    ProcessAccessLinkResult, RegisterResult, RegisterUserInput, UpdateUserIntegrationInput,
+    UpdateUserNotificationPlatformInput,
 };
 use traits::AuthProvider;
+use user_models::UpdateUserInput;
 use user_service::UserService;
 
 #[derive(Default)]
diff --git a/crates/services/cache/src/lib.rs b/crates/services/cache/src/lib.rs
index 1614fc12e3..727c2784e4 100644
--- a/crates/services/cache/src/lib.rs
+++ b/crates/services/cache/src/lib.rs
@@ -1,6 +1,6 @@
 use async_graphql::Result;
 use chrono::{Duration, Utc};
-use common_models::ApplicationCacheKey;
+use common_models::{ApplicationCacheKey, ApplicationCacheValue};
 use common_utils::ryot_log;
 use database_models::{application_cache, prelude::ApplicationCache};
 use sea_orm::{ActiveValue, ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter};
@@ -21,19 +21,22 @@ impl CacheService {
     pub async fn set_with_expiry(
         &self,
         key: ApplicationCacheKey,
-        expiry_hours: i64,
+        expiry_hours: Option<i64>,
+        value: ApplicationCacheValue,
     ) -> Result<Uuid> {
         let now = Utc::now();
         let to_insert = application_cache::ActiveModel {
             key: ActiveValue::Set(key),
-            expires_at: ActiveValue::Set(Some(now + Duration::hours(expiry_hours))),
+            value: ActiveValue::Set(value),
             created_at: ActiveValue::Set(now),
+            expires_at: ActiveValue::Set(expiry_hours.map(|hours| now + Duration::hours(hours))),
             ..Default::default()
         };
         let inserted = ApplicationCache::insert(to_insert)
             .on_conflict(
                 OnConflict::column(application_cache::Column::Key)
                     .update_columns([
+                        application_cache::Column::Value,
                         application_cache::Column::ExpiresAt,
                         application_cache::Column::CreatedAt,
                     ])
@@ -46,7 +49,7 @@ impl CacheService {
         Ok(insert_id)
     }
 
-    pub async fn get(&self, key: ApplicationCacheKey) -> Result<Option<()>> {
+    pub async fn get_key(&self, key: ApplicationCacheKey) -> Result<Option<ApplicationCacheValue>> {
         let cache = ApplicationCache::find()
             .filter(application_cache::Column::Key.eq(key))
             .one(&self.db)
@@ -55,14 +58,18 @@ impl CacheService {
             .filter(|cache| {
                 cache
                     .expires_at
-                    .map_or(false, |expires_at| expires_at > Utc::now())
+                    .map_or(true, |expires_at| expires_at > Utc::now())
             })
-            .map(|_| ()))
+            .map(|m| m.value))
     }
 
-    pub async fn delete(&self, key: ApplicationCacheKey) -> Result<bool> {
-        let deleted = ApplicationCache::delete_many()
+    pub async fn expire_key(&self, key: ApplicationCacheKey) -> Result<bool> {
+        let deleted = ApplicationCache::update_many()
             .filter(application_cache::Column::Key.eq(key))
+            .set(application_cache::ActiveModel {
+                expires_at: ActiveValue::Set(Some(Utc::now())),
+                ..Default::default()
+            })
             .exec(&self.db)
             .await?;
         Ok(deleted.rows_affected > 0)
diff --git a/crates/services/collection/Cargo.toml b/crates/services/collection/Cargo.toml
index 50b336bd56..c9c7a9dece 100644
--- a/crates/services/collection/Cargo.toml
+++ b/crates/services/collection/Cargo.toml
@@ -10,6 +10,7 @@ common-models = { path = "../../models/common" }
 common-utils = { path = "../../utils/common" }
 database-models = { path = "../../models/database" }
 database-utils = { path = "../../utils/database" }
+dependent-utils = { path = "../../utils/dependent" }
 dependent-models = { path = "../../models/dependent" }
 enums = { path = "../../enums" }
 media-models = { path = "../../models/media" }
diff --git a/crates/services/collection/src/lib.rs b/crates/services/collection/src/lib.rs
index 9602b01026..fb93aaae98 100644
--- a/crates/services/collection/src/lib.rs
+++ b/crates/services/collection/src/lib.rs
@@ -15,10 +15,10 @@ use database_models::{
     user_to_entity,
 };
 use database_utils::{
-    add_entity_to_collection, create_or_update_collection, ilike_sql, item_reviews,
-    remove_entity_from_collection,
+    create_or_update_collection, ilike_sql, item_reviews, remove_entity_from_collection,
 };
 use dependent_models::{CollectionContents, SearchResults};
+use dependent_utils::add_entity_to_collection;
 use enums::EntityLot;
 use media_models::{
     CollectionContentsInput, CollectionContentsSortBy, CollectionItem,
diff --git a/crates/services/fitness/src/lib.rs b/crates/services/fitness/src/lib.rs
index fe1c409b3a..26697ed3a7 100644
--- a/crates/services/fitness/src/lib.rs
+++ b/crates/services/fitness/src/lib.rs
@@ -8,13 +8,13 @@ use common_utils::{ryot_log, PAGE_SIZE};
 use database_models::{
     collection_to_entity, exercise,
     prelude::{
-        CollectionToEntity, Exercise, UserMeasurement, UserToEntity, Workout, WorkoutTemplate,
+        CollectionToEntity, Exercise, User, UserMeasurement, UserToEntity, Workout, WorkoutTemplate,
     },
-    user_measurement, user_to_entity, workout, workout_template,
+    user, user_measurement, user_to_entity, workout, workout_template,
 };
 use database_utils::{
-    deploy_job_to_re_evaluate_user_workouts, entity_in_collections, ilike_sql, item_reviews,
-    pro_instance_guard, user_measurements_list, user_workout_details,
+    entity_in_collections, ilike_sql, item_reviews, schedule_user_for_workout_revision,
+    server_key_validation_guard, user_measurements_list, user_workout_details,
     user_workout_template_details,
 };
 use dependent_models::{
@@ -39,7 +39,9 @@ use sea_orm::{
     ItemsAndPagesNumber, ModelTrait, PaginatorTrait, QueryFilter, QueryOrder, QuerySelect,
     QueryTrait, RelationTrait,
 };
-use sea_query::{extension::postgres::PgExpr, Alias, Condition, Expr, Func, JoinType, OnConflict};
+use sea_query::{
+    extension::postgres::PgExpr, Alias, Condition, Expr, Func, JoinType, OnConflict, PgFunc,
+};
 use slug::slugify;
 use supporting_service::SupportingService;
 
@@ -47,21 +49,24 @@ const EXERCISE_DB_URL: &str = "https://raw.githubusercontent.com/yuhonas/free-ex
 const JSON_URL: &str = const_str::concat!(EXERCISE_DB_URL, "/dist/exercises.json");
 const IMAGES_PREFIX_URL: &str = const_str::concat!(EXERCISE_DB_URL, "/exercises");
 
-pub struct ExerciseService(pub Arc<SupportingService>);
+pub struct FitnessService(pub Arc<SupportingService>);
 
-impl ExerciseService {
+impl FitnessService {
     pub async fn user_workout_templates_list(
         &self,
         user_id: String,
         input: SearchInput,
-    ) -> Result<SearchResults<workout_template::Model>> {
+    ) -> Result<SearchResults<String>> {
         let page = input.page.unwrap_or(1);
         let paginator = WorkoutTemplate::find()
+            .select_only()
+            .column(workout_template::Column::Id)
             .filter(workout_template::Column::UserId.eq(user_id))
             .apply_if(input.query, |query, v| {
                 query.filter(Expr::col(workout_template::Column::Name).ilike(ilike_sql(&v)))
             })
             .order_by_desc(workout_template::Column::CreatedOn)
+            .into_tuple::<String>()
             .paginate(&self.0.db, PAGE_SIZE.try_into().unwrap());
         let ItemsAndPagesNumber {
             number_of_items,
@@ -94,7 +99,7 @@ impl ExerciseService {
         user_id: String,
         input: UserWorkoutInput,
     ) -> Result<String> {
-        pro_instance_guard(self.0.is_pro).await?;
+        server_key_validation_guard(self.0.is_server_key_validated().await?).await?;
         let mut summary = WorkoutSummary::default();
         let mut information = WorkoutInformation {
             assets: None,
@@ -105,7 +110,7 @@ impl ExerciseService {
         for exercise in input.exercises {
             let db_ex = self.exercise_details(exercise.exercise_id.clone()).await?;
             summary.exercises.push(WorkoutSummaryExercise {
-                name: exercise.exercise_id.clone(),
+                id: exercise.exercise_id.clone(),
                 best_set: None,
                 lot: None,
                 num_sets: exercise.sets.len(),
@@ -129,7 +134,7 @@ impl ExerciseService {
                     })
                     .collect(),
                 notes: exercise.notes,
-                name: exercise.exercise_id,
+                id: exercise.exercise_id,
             });
         }
         let processed_exercises = information.exercises.clone();
@@ -167,7 +172,7 @@ impl ExerciseService {
         user_id: String,
         workout_template_id: String,
     ) -> Result<bool> {
-        pro_instance_guard(self.0.is_pro).await?;
+        server_key_validation_guard(self.0.is_server_key_validated().await?).await?;
         let Some(wkt) = WorkoutTemplate::find_by_id(workout_template_id)
             .filter(workout_template::Column::UserId.eq(&user_id))
             .one(&self.0.db)
@@ -256,14 +261,17 @@ impl ExerciseService {
         &self,
         user_id: String,
         input: SearchInput,
-    ) -> Result<SearchResults<workout::Model>> {
+    ) -> Result<SearchResults<String>> {
         let page = input.page.unwrap_or(1);
         let paginator = Workout::find()
+            .select_only()
+            .column(workout::Column::Id)
             .filter(workout::Column::UserId.eq(user_id))
             .apply_if(input.query, |query, v| {
                 query.filter(Expr::col(workout::Column::Name).ilike(ilike_sql(&v)))
             })
             .order_by_desc(workout::Column::EndTime)
+            .into_tuple::<String>()
             .paginate(&self.0.db, PAGE_SIZE.try_into().unwrap());
         let ItemsAndPagesNumber {
             number_of_items,
@@ -333,13 +341,7 @@ impl ExerciseService {
                 query
                     .apply_if(q.lot, |q, v| q.filter(exercise::Column::Lot.eq(v)))
                     .apply_if(q.muscle, |q, v| {
-                        q.filter(
-                            Expr::expr(Func::cast_as(
-                                Expr::col(exercise::Column::Muscles),
-                                Alias::new("text"),
-                            ))
-                            .ilike(ilike_sql(&v.to_string())),
-                        )
+                        q.filter(Expr::val(v).eq(PgFunc::any(Expr::col(exercise::Column::Muscles))))
                     })
                     .apply_if(q.level, |q, v| q.filter(exercise::Column::Level.eq(v)))
                     .apply_if(q.force, |q, v| q.filter(exercise::Column::Force.eq(v)))
@@ -361,7 +363,7 @@ impl ExerciseService {
                             Expr::col((AliasedExercise::Table, AliasedExercise::Id))
                                 .ilike(ilike_sql(&v)),
                         )
-                        .add(Expr::col(exercise::Column::Identifier).ilike(slugify(v))),
+                        .add(Expr::col(exercise::Column::Name).ilike(slugify(v))),
                 )
             })
             .join(
@@ -410,6 +412,13 @@ impl ExerciseService {
     }
 
     pub async fn deploy_update_exercise_library_job(&self) -> Result<bool> {
+        if Exercise::find().count(&self.0.db).await? > 0 {
+            return Ok(true);
+        }
+        ryot_log!(
+            info,
+            "Instance does not have exercises data. Deploying job to download them..."
+        );
         let exercises = self.get_all_exercises_from_dataset().await?;
         for exercise in exercises {
             self.0
@@ -433,7 +442,7 @@ impl ExerciseService {
         let mut muscles = ex.attributes.primary_muscles;
         muscles.extend(ex.attributes.secondary_muscles);
         if let Some(e) = Exercise::find()
-            .filter(exercise::Column::Identifier.eq(&ex.identifier))
+            .filter(exercise::Column::Id.eq(&ex.name))
             .filter(exercise::Column::Source.eq(ExerciseSource::Github))
             .one(&self.0.db)
             .await?
@@ -441,7 +450,7 @@ impl ExerciseService {
             ryot_log!(
                 debug,
                 "Updating existing exercise with identifier: {}",
-                ex.identifier
+                ex.name
             );
             let mut db_ex: exercise::ActiveModel = e.into();
             db_ex.attributes = ActiveValue::Set(attributes);
@@ -459,17 +468,17 @@ impl ExerciseService {
                 | ExerciseCategory::Powerlifting => ExerciseLot::RepsAndWeight,
             };
             let db_exercise = exercise::ActiveModel {
-                id: ActiveValue::Set(ex.name),
-                source: ActiveValue::Set(ExerciseSource::Github),
-                identifier: ActiveValue::Set(Some(ex.identifier)),
+                lot: ActiveValue::Set(lot),
                 muscles: ActiveValue::Set(muscles),
+                id: ActiveValue::Set(ex.name.clone()),
+                name: ActiveValue::Set(ex.name.clone()),
                 attributes: ActiveValue::Set(attributes),
-                lot: ActiveValue::Set(lot),
+                created_by_user_id: ActiveValue::Set(None),
                 level: ActiveValue::Set(ex.attributes.level),
                 force: ActiveValue::Set(ex.attributes.force),
+                source: ActiveValue::Set(ExerciseSource::Github),
                 equipment: ActiveValue::Set(ex.attributes.equipment),
                 mechanic: ActiveValue::Set(ex.attributes.mechanic),
-                created_by_user_id: ActiveValue::Set(None),
             };
             let created_exercise = db_exercise.insert(&self.0.db).await?;
             ryot_log!(
@@ -543,7 +552,7 @@ impl ExerciseService {
         }
         if new_wkt.is_changed() {
             new_wkt.update(&self.0.db).await?;
-            deploy_job_to_re_evaluate_user_workouts(&user_id, &self.0).await;
+            schedule_user_for_workout_revision(&user_id, &self.0).await?;
             Ok(true)
         } else {
             Ok(false)
@@ -569,7 +578,7 @@ impl ExerciseService {
         for (idx, ex) in wkt.information.exercises.iter().enumerate() {
             let Some(association) = UserToEntity::find()
                 .filter(user_to_entity::Column::UserId.eq(&user_id))
-                .filter(user_to_entity::Column::ExerciseId.eq(ex.name.clone()))
+                .filter(user_to_entity::Column::ExerciseId.eq(ex.id.clone()))
                 .one(&self.0.db)
                 .await?
             else {
@@ -591,11 +600,11 @@ impl ExerciseService {
             association.update(&self.0.db).await?;
         }
         wkt.delete(&self.0.db).await?;
-        deploy_job_to_re_evaluate_user_workouts(&user_id, &self.0).await;
+        schedule_user_for_workout_revision(&user_id, &self.0).await?;
         Ok(true)
     }
 
-    pub async fn re_evaluate_user_workouts(&self, user_id: String) -> Result<()> {
+    pub async fn revise_user_workouts(&self, user_id: String) -> Result<()> {
         UserToEntity::delete_many()
             .filter(user_to_entity::Column::UserId.eq(&user_id))
             .filter(user_to_entity::Column::ExerciseId.is_not_null())
@@ -612,7 +621,7 @@ impl ExerciseService {
             let workout_input = db_workout_to_workout_input(workout);
             self.create_or_update_user_workout(&user_id, workout_input)
                 .await?;
-            ryot_log!(debug, "Re-evaluated workout: {}/{}", idx + 1, total);
+            ryot_log!(debug, "Revised workout: {}/{}", idx + 1, total);
         }
         Ok(())
     }
@@ -629,8 +638,8 @@ impl ExerciseService {
                 .unwrap();
             let mut summary = db_workout.summary.clone();
             let mut information = db_workout.information.clone();
-            summary.exercises[workout.idx].name = new_name.clone();
-            information.exercises[workout.idx].name = new_name.clone();
+            summary.exercises[workout.idx].id = new_name.clone();
+            information.exercises[workout.idx].id = new_name.clone();
             let mut db_workout: workout::ActiveModel = db_workout.into();
             db_workout.summary = ActiveValue::Set(summary);
             db_workout.information = ActiveValue::Set(information);
@@ -646,11 +655,11 @@ impl ExerciseService {
     ) -> Result<bool> {
         let entity = UserToEntity::find()
             .filter(user_to_entity::Column::UserId.eq(&user_id))
-            .filter(user_to_entity::Column::ExerciseId.eq(input.old_name.clone()))
+            .filter(user_to_entity::Column::ExerciseId.eq(input.old_id.clone()))
             .one(&self.0.db)
             .await?
             .ok_or_else(|| Error::new("Exercise does not exist"))?;
-        let old_exercise = Exercise::find_by_id(input.old_name.clone())
+        let old_exercise = Exercise::find_by_id(input.old_id.clone())
             .one(&self.0.db)
             .await?
             .unwrap();
@@ -668,21 +677,13 @@ impl ExerciseService {
             old_exercise.delete(&self.0.db).await?;
             return Ok(true);
         }
-        if input.old_name != input.update.id {
-            if Exercise::find_by_id(input.update.id.clone())
+        if input.old_id != input.update.id
+            && Exercise::find_by_id(input.update.id.clone())
                 .one(&self.0.db)
                 .await?
                 .is_some()
-            {
-                return Err(Error::new("Exercise with the new name already exists."));
-            }
-            Exercise::update_many()
-                .col_expr(exercise::Column::Id, Expr::value(input.update.id.clone()))
-                .filter(exercise::Column::Id.eq(input.old_name.clone()))
-                .exec(&self.0.db)
-                .await?;
-            self.change_exercise_name_in_history(input.update.id.clone(), entity)
-                .await?;
+        {
+            return Err(Error::new("Exercise with the new name already exists."));
         }
         for image in old_exercise.attributes.internal_images {
             if let StoredUrl::S3(key) = image {
@@ -691,7 +692,6 @@ impl ExerciseService {
         }
         self.create_custom_exercise(&user_id, input.update.clone())
             .await?;
-        deploy_job_to_re_evaluate_user_workouts(&user_id, &self.0).await;
         Ok(true)
     }
 
@@ -721,21 +721,31 @@ impl ExerciseService {
             }
         };
         let mut exercise_extra_information = ute.clone().exercise_extra_information.unwrap();
-        let (left, right) = input.change.property.split_once('.').ok_or_else(err)?;
-        match left {
-            "set_rest_timers" => {
-                let value = input.change.value.parse().unwrap();
-                let set_rest_timers = &mut exercise_extra_information.settings.set_rest_timers;
-                match right {
-                    "drop" => set_rest_timers.drop = Some(value),
-                    "normal" => set_rest_timers.normal = Some(value),
-                    "warmup" => set_rest_timers.warmup = Some(value),
-                    "failure" => set_rest_timers.failure = Some(value),
-                    _ => return Err(err()),
+        if input.change.property.contains('.') {
+            let (left, right) = input.change.property.split_once('.').ok_or_else(err)?;
+            match left {
+                "set_rest_timers" => {
+                    let value = input.change.value.parse().unwrap();
+                    let set_rest_timers = &mut exercise_extra_information.settings.set_rest_timers;
+                    match right {
+                        "drop" => set_rest_timers.drop = Some(value),
+                        "normal" => set_rest_timers.normal = Some(value),
+                        "warmup" => set_rest_timers.warmup = Some(value),
+                        "failure" => set_rest_timers.failure = Some(value),
+                        _ => return Err(err()),
+                    }
                 }
+                _ => return Err(err()),
+            };
+        } else {
+            match input.change.property.as_str() {
+                "exclude_from_analytics" => {
+                    exercise_extra_information.settings.exclude_from_analytics =
+                        input.change.value.parse().unwrap();
+                }
+                _ => return Err(err()),
             }
-            _ => return Err(err()),
-        };
+        }
         let mut ute: user_to_entity::ActiveModel = ute.into();
         ute.exercise_extra_information = ActiveValue::Set(Some(exercise_extra_information));
         ute.update(&self.0.db).await?;
@@ -773,7 +783,27 @@ impl ExerciseService {
             .ok_or_else(|| Error::new("Exercise does not exist"))?;
         self.change_exercise_name_in_history(merge_into, old_entity)
             .await?;
-        deploy_job_to_re_evaluate_user_workouts(&user_id, &self.0).await;
+        schedule_user_for_workout_revision(&user_id, &self.0).await?;
         Ok(true)
     }
+
+    pub async fn process_users_scheduled_for_workout_revision(&self) -> Result<()> {
+        let revisions = User::find()
+            .filter(Expr::cust(
+                "(extra_information -> 'scheduled_for_workout_revision')::boolean = true",
+            ))
+            .all(&self.0.db)
+            .await?;
+        for user in revisions {
+            ryot_log!(debug, "Revising workouts for {}", user.id);
+            self.revise_user_workouts(user.id.clone()).await?;
+            let mut extra_information = user.extra_information.clone().unwrap_or_default();
+            extra_information.scheduled_for_workout_revision = false;
+            let mut user: user::ActiveModel = user.into();
+            user.extra_information = ActiveValue::Set(Some(extra_information));
+            user.update(&self.0.db).await?;
+        }
+        ryot_log!(debug, "Completed scheduled workout revisions");
+        Ok(())
+    }
 }
diff --git a/crates/services/importer/Cargo.toml b/crates/services/importer/Cargo.toml
index c28a56c420..2cb48e7368 100644
--- a/crates/services/importer/Cargo.toml
+++ b/crates/services/importer/Cargo.toml
@@ -28,7 +28,6 @@ importer-models = { path = "../../models/importer" }
 indexmap = { workspace = true }
 itertools = { workspace = true }
 media-models = { path = "../../models/media" }
-nanoid = { workspace = true }
 providers = { path = "../../providers" }
 reqwest = { workspace = true }
 rust_decimal = { workspace = true }
diff --git a/crates/services/importer/src/lib.rs b/crates/services/importer/src/lib.rs
index 1527da6ed2..2f4d7d1878 100644
--- a/crates/services/importer/src/lib.rs
+++ b/crates/services/importer/src/lib.rs
@@ -75,7 +75,9 @@ impl ImporterService {
         let import_id = db_import_job.id.clone();
         ryot_log!(debug, "Started import job with id {import_id}");
         let maybe_import = match input.source {
-            ImportSource::StrongApp => strong_app::import(input.strong_app.unwrap(), &self.0).await,
+            ImportSource::StrongApp => {
+                strong_app::import(input.strong_app.unwrap(), &self.0, &user_id).await
+            }
             ImportSource::Mediatracker => mediatracker::import(input.url_and_key.unwrap()).await,
             ImportSource::Myanimelist => myanimelist::import(input.mal.unwrap()).await,
             ImportSource::Goodreads => {
diff --git a/crates/services/importer/src/plex.rs b/crates/services/importer/src/plex.rs
index 388906fb67..f7465d92c0 100644
--- a/crates/services/importer/src/plex.rs
+++ b/crates/services/importer/src/plex.rs
@@ -1,6 +1,5 @@
 use application_utils::get_base_http_client;
 use async_graphql::Result;
-use common_models::StringIdObject;
 use common_utils::ryot_log;
 use dependent_models::{ImportCompletedItem, ImportResult};
 use enums::{ImportSource, MediaLot, MediaSource};
@@ -9,46 +8,7 @@ use media_models::{
     DeployUrlAndKeyImportInput, ImportOrExportMetadataItem, ImportOrExportMetadataItemSeen,
 };
 use reqwest::header::{HeaderName, HeaderValue, ACCEPT};
-use sea_orm::prelude::DateTimeUtc;
-use serde::{Deserialize, Serialize};
-use serde_with::{formats::Flexible, serde_as, TimestampSeconds};
-
-#[serde_as]
-#[derive(Debug, Deserialize, Serialize)]
-struct PlexMetadataItem {
-    title: String,
-    #[serde(rename = "type")]
-    item_type: String,
-    #[serde(rename = "ratingKey")]
-    rating_key: Option<String>,
-    key: String,
-    #[serde(rename = "Guid")]
-    guid: Option<Vec<StringIdObject>>,
-    #[serde_as(as = "Option<TimestampSeconds<i64, Flexible>>")]
-    #[serde(rename = "lastViewedAt")]
-    last_viewed_at: Option<DateTimeUtc>,
-    index: Option<i32>,
-    #[serde(rename = "parentIndex")]
-    parent_index: Option<i32>,
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-#[serde(rename_all = "PascalCase")]
-struct PlexLibrary {
-    pub directory: Vec<PlexMetadataItem>,
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-#[serde(rename_all = "PascalCase")]
-struct PlexMetadata {
-    pub metadata: Vec<PlexMetadataItem>,
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-#[serde(rename_all = "PascalCase")]
-struct PlexMediaResponse<T> {
-    pub media_container: T,
-}
+use specific_models::plex as plex_models;
 
 pub async fn import(input: DeployUrlAndKeyImportInput) -> Result<ImportResult> {
     let client = get_base_http_client(Some(vec![
@@ -62,7 +22,7 @@ pub async fn import(input: DeployUrlAndKeyImportInput) -> Result<ImportResult> {
         .get(format!("{}/library/sections", input.api_url))
         .send()
         .await?
-        .json::<PlexMediaResponse<PlexLibrary>>()
+        .json::<plex_models::PlexMediaResponse<plex_models::PlexLibrary>>()
         .await?;
 
     let mut success_items = vec![];
@@ -87,12 +47,13 @@ pub async fn import(input: DeployUrlAndKeyImportInput) -> Result<ImportResult> {
             .query(&serde_json::json!({ "includeGuids": "1" }))
             .send()
             .await?
-            .json::<PlexMediaResponse<PlexMetadata>>()
+            .json::<plex_models::PlexMediaResponse<plex_models::PlexMetadata>>()
             .await?;
-        for item in items.media_container.metadata {
+        for (idx, item) in items.media_container.metadata.into_iter().enumerate() {
             let Some(_lv) = item.last_viewed_at else {
                 continue;
             };
+            ryot_log!(debug, "Processing item {}", idx + 1);
             let gu_ids = item.guid.unwrap_or_default();
             let Some(tmdb_id) = gu_ids
                 .iter()
@@ -132,7 +93,7 @@ pub async fn import(input: DeployUrlAndKeyImportInput) -> Result<ImportResult> {
                         ))
                         .send()
                         .await?
-                        .json::<PlexMediaResponse<PlexMetadata>>()
+                        .json::<plex_models::PlexMediaResponse<plex_models::PlexMetadata>>()
                         .await?;
                     let mut item = ImportOrExportMetadataItem {
                         lot,
diff --git a/crates/services/importer/src/strong_app.rs b/crates/services/importer/src/strong_app.rs
index af2d1b984a..8db1804847 100644
--- a/crates/services/importer/src/strong_app.rs
+++ b/crates/services/importer/src/strong_app.rs
@@ -6,7 +6,8 @@ use common_utils::ryot_log;
 use csv::ReaderBuilder;
 use database_models::{exercise, prelude::Exercise};
 use dependent_models::{ImportCompletedItem, ImportResult};
-use enums::ExerciseLot;
+use dependent_utils::generate_exercise_id;
+use enums::{ExerciseLot, ExerciseSource};
 use fitness_models::{
     SetLot, UserExerciseInput, UserWorkoutInput, UserWorkoutSetRecord, WorkoutSetStatistic,
 };
@@ -14,7 +15,6 @@ use importer_models::{ImportFailStep, ImportFailedItem};
 use indexmap::IndexMap;
 use itertools::Itertools;
 use media_models::DeployStrongAppImportInput;
-use nanoid::nanoid;
 use rust_decimal::Decimal;
 use rust_decimal_macros::dec;
 use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
@@ -51,20 +51,18 @@ struct Entry {
 pub async fn import(
     input: DeployStrongAppImportInput,
     ss: &Arc<SupportingService>,
+    user_id: &str,
 ) -> Result<ImportResult> {
     let mut completed = vec![];
     let mut failed = vec![];
     if let Some(csv_path) = input.data_export_path {
-        import_exercises(csv_path, ss, &mut failed, &mut completed).await?;
+        import_exercises(user_id, csv_path, ss, &mut failed, &mut completed).await?;
     }
-    Ok(ImportResult {
-        failed,
-        completed,
-        ..Default::default()
-    })
+    Ok(ImportResult { failed, completed })
 }
 
 async fn import_exercises(
+    user_id: &str,
     csv_path: String,
     ss: &Arc<SupportingService>,
     failed: &mut Vec<ImportFailedItem>,
@@ -147,44 +145,57 @@ async fn import_exercises(
                 continue;
             };
             let existing_exercise = Exercise::find()
-                .filter(exercise::Column::Id.eq(&exercise_name))
                 .filter(exercise::Column::Lot.eq(exercise_lot))
+                .filter(exercise::Column::Name.eq(&exercise_name))
                 .one(&ss.db)
                 .await?;
-            let exercise_id = if let Some(db_ex) = existing_exercise {
-                db_ex.id
-            } else if let Some(mem_ex) = unique_exercises.get(&exercise_name) {
-                mem_ex.id.clone()
-            } else {
-                let id = format!("{} [{}]", exercise_name, nanoid!(5));
-                unique_exercises.insert(
-                    exercise_name.clone(),
-                    exercise::Model {
-                        id: id.clone(),
-                        lot: exercise_lot,
-                        ..Default::default()
-                    },
-                );
-                id
+            let generated_id = generate_exercise_id(&exercise_name, exercise_lot, user_id);
+            let exercise_id = match existing_exercise {
+                Some(db_ex)
+                    if db_ex.source == ExerciseSource::Github || db_ex.id == generated_id =>
+                {
+                    db_ex.id
+                }
+                _ => match unique_exercises.get(&exercise_name) {
+                    Some(mem_ex) => mem_ex.id.clone(),
+                    None => {
+                        unique_exercises.insert(
+                            exercise_name.clone(),
+                            exercise::Model {
+                                lot: exercise_lot,
+                                name: exercise_name,
+                                id: generated_id.clone(),
+                                ..Default::default()
+                            },
+                        );
+                        generated_id
+                    }
+                },
             };
             ryot_log!(debug, "Importing exercise with id = {}", exercise_id);
-            for sets in exercises {
-                if let Some(note) = sets.notes {
+            for set in exercises {
+                if let Some(note) = set.notes {
                     notes.push(note);
                 }
-                let weight = sets.weight.map(|d| if d == dec!(0) { dec!(1) } else { d });
+                let weight = set.weight.map(|d| if d == dec!(0) { dec!(1) } else { d });
+                let set_lot = match set.set_order.as_str() {
+                    "W" => SetLot::WarmUp,
+                    "F" => SetLot::Failure,
+                    "D" => SetLot::Drop,
+                    _ => SetLot::Normal,
+                };
                 collected_sets.push(UserWorkoutSetRecord {
                     statistic: WorkoutSetStatistic {
                         weight,
-                        reps: sets.reps,
-                        duration: sets.seconds.and_then(|r| r.checked_div(dec!(60))),
-                        distance: sets.distance.and_then(|d| d.checked_div(dec!(1000))),
+                        reps: set.reps,
+                        duration: set.seconds.and_then(|r| r.checked_div(dec!(60))),
+                        distance: set.distance.and_then(|d| d.checked_div(dec!(1000))),
                         ..Default::default()
                     },
                     note: None,
+                    lot: set_lot,
                     rest_time: None,
                     confirmed_at: None,
-                    lot: SetLot::Normal,
                 });
             }
             collected_exercises.push(UserExerciseInput {
diff --git a/crates/services/integration/src/lib.rs b/crates/services/integration/src/lib.rs
index cf10e1ddbd..a19380d858 100644
--- a/crates/services/integration/src/lib.rs
+++ b/crates/services/integration/src/lib.rs
@@ -1,4 +1,4 @@
-use std::sync::Arc;
+use std::{collections::HashSet, sync::Arc};
 
 use async_graphql::{Error, Result as GqlResult};
 use chrono::Utc;
@@ -14,10 +14,8 @@ use dependent_utils::{commit_metadata, process_import};
 use enums::{EntityLot, IntegrationLot, IntegrationProvider, MediaLot};
 use media_models::SeenShowExtraInformation;
 use providers::google_books::GoogleBooksService;
-use push::jellyfin::JellyfinPushIntegration;
 use rust_decimal_macros::dec;
 use sea_orm::{ActiveModelTrait, ActiveValue, ColumnTrait, EntityTrait, QueryFilter, QuerySelect};
-use sink::generic_json::GenericJsonSinkIntegration;
 use supporting_service::SupportingService;
 use traits::TraceOk;
 use uuid::Uuid;
@@ -27,15 +25,6 @@ mod sink;
 mod utils;
 mod yank;
 
-use crate::{
-    push::{radarr::RadarrPushIntegration, sonarr::SonarrPushIntegration},
-    sink::{
-        emby::EmbySinkIntegration, jellyfin::JellyfinSinkIntegration, kodi::KodiSinkIntegration,
-        plex::PlexSinkIntegration,
-    },
-    yank::{audiobookshelf::AudiobookshelfYankIntegration, komga::KomgaYankIntegration},
-};
-
 pub struct IntegrationService(pub Arc<SupportingService>);
 
 impl IntegrationService {
@@ -110,28 +99,14 @@ impl IntegrationService {
             return Err(Error::new("Integration is disabled".to_owned()));
         }
         let maybe_progress_update = match integration.provider {
-            IntegrationProvider::Kodi => {
-                let kodi = KodiSinkIntegration::new(payload);
-                kodi.yank_progress().await
-            }
-            IntegrationProvider::Emby => {
-                let emby = EmbySinkIntegration::new(payload, self.0.db.clone());
-                emby.yank_progress().await
-            }
-            IntegrationProvider::JellyfinSink => {
-                let jellyfin = JellyfinSinkIntegration::new(payload);
-                jellyfin.yank_progress().await
-            }
-            IntegrationProvider::Plex => {
+            IntegrationProvider::Kodi => sink::kodi::yank_progress(payload).await,
+            IntegrationProvider::Emby => sink::emby::yank_progress(payload, &self.0.db).await,
+            IntegrationProvider::JellyfinSink => sink::jellyfin::yank_progress(payload).await,
+            IntegrationProvider::PlexSink => {
                 let specifics = integration.clone().provider_specifics.unwrap();
-                let plex =
-                    PlexSinkIntegration::new(payload, specifics.plex_username, self.0.db.clone());
-                plex.yank_progress().await
-            }
-            IntegrationProvider::GenericJson => {
-                let generic_json = GenericJsonSinkIntegration::new(payload);
-                generic_json.yank_progress().await
+                sink::plex::yank_progress(payload, &self.0.db, specifics.plex_sink_username).await
             }
+            IntegrationProvider::GenericJson => sink::generic_json::yank_progress(payload).await,
             _ => return Err(Error::new("Unsupported integration source".to_owned())),
         };
         match maybe_progress_update {
@@ -199,7 +174,7 @@ impl IntegrationService {
                 if let Some(entity_id) = maybe_entity_id {
                     let _push_result = match integration.provider {
                         IntegrationProvider::Radarr => {
-                            let radarr = RadarrPushIntegration::new(
+                            push::radarr::push_progress(
                                 specifics.radarr_api_key.unwrap(),
                                 specifics.radarr_profile_id.unwrap(),
                                 entity_id,
@@ -207,11 +182,11 @@ impl IntegrationService {
                                 metadata.lot,
                                 metadata.title,
                                 specifics.radarr_root_folder_path.unwrap(),
-                            );
-                            radarr.push_progress().await
+                            )
+                            .await
                         }
                         IntegrationProvider::Sonarr => {
-                            let sonarr = SonarrPushIntegration::new(
+                            push::sonarr::push_progress(
                                 specifics.sonarr_api_key.unwrap(),
                                 specifics.sonarr_profile_id.unwrap(),
                                 entity_id,
@@ -219,8 +194,8 @@ impl IntegrationService {
                                 metadata.lot,
                                 metadata.title,
                                 specifics.sonarr_root_folder_path.unwrap(),
-                            );
-                            sonarr.push_progress().await
+                            )
+                            .await
                         }
                         _ => unreachable!(),
                     };
@@ -250,15 +225,15 @@ impl IntegrationService {
             let specifics = integration.provider_specifics.unwrap();
             match integration.provider {
                 IntegrationProvider::JellyfinPush => {
-                    let integration = JellyfinPushIntegration::new(
+                    push::jellyfin::push_progress(
                         specifics.jellyfin_push_base_url.unwrap(),
                         specifics.jellyfin_push_username.unwrap(),
                         specifics.jellyfin_push_password.unwrap(),
                         &metadata_lot,
                         &metadata_title,
                         &show_extra_information,
-                    );
-                    integration.push_progress().await?;
+                    )
+                    .await?;
                 }
                 _ => unreachable!(),
             }
@@ -277,7 +252,6 @@ impl IntegrationService {
             .all(&self.0.db)
             .await?;
         let mut progress_updates = vec![];
-        let mut to_update_integrations = vec![];
         for integration in integrations.into_iter() {
             if integration.is_disabled.unwrap_or_default() {
                 ryot_log!(debug, "Integration {} is disabled", integration.id);
@@ -286,31 +260,27 @@ impl IntegrationService {
             let specifics = integration.clone().provider_specifics.unwrap();
             let response = match integration.provider {
                 IntegrationProvider::Audiobookshelf => {
-                    let audiobookshelf = AudiobookshelfYankIntegration::new(
+                    yank::audiobookshelf::yank_progress(
                         specifics.audiobookshelf_base_url.unwrap(),
                         specifics.audiobookshelf_token.unwrap(),
-                        integration.sync_to_owned_collection,
                         GoogleBooksService::new(&self.0.config.books.google_books).await,
-                    );
-                    audiobookshelf
-                        .yank_progress(|input| commit_metadata(input, &self.0))
-                        .await
+                        |input| commit_metadata(input, &self.0),
+                    )
+                    .await
                 }
                 IntegrationProvider::Komga => {
-                    let komga = KomgaYankIntegration::new(
+                    yank::komga::yank_progress(
                         specifics.komga_base_url.unwrap(),
                         specifics.komga_username.unwrap(),
                         specifics.komga_password.unwrap(),
                         specifics.komga_provider.unwrap(),
-                        self.0.db.clone(),
-                        integration.sync_to_owned_collection,
-                    );
-                    komga.yank_progress().await
+                        &self.0.db,
+                    )
+                    .await
                 }
                 _ => continue,
             };
             if let Ok(update) = response {
-                to_update_integrations.push(integration.id.clone());
                 progress_updates.push((integration, update));
             }
         }
@@ -329,11 +299,94 @@ impl IntegrationService {
             .column(integration::Column::UserId)
             .into_tuple::<String>()
             .all(&self.0.db)
-            .await?;
+            .await?
+            .into_iter()
+            .collect::<HashSet<String>>();
         for user_id in users_with_integrations {
             ryot_log!(debug, "Yanking integrations data for user {}", user_id);
             self.yank_integrations_data_for_user(&user_id).await?;
         }
         Ok(())
     }
+
+    pub async fn sync_integrations_data_to_owned_collection_for_user(
+        &self,
+        user_id: &String,
+    ) -> GqlResult<bool> {
+        let preferences = user_by_id(user_id, &self.0).await?.preferences;
+        if preferences.general.disable_integrations {
+            return Ok(false);
+        }
+        let integrations = Integration::find()
+            .filter(integration::Column::UserId.eq(user_id))
+            .filter(integration::Column::SyncToOwnedCollection.eq(true))
+            .all(&self.0.db)
+            .await?;
+        let mut progress_updates = vec![];
+        for integration in integrations.into_iter() {
+            if integration.is_disabled.unwrap_or_default() {
+                ryot_log!(debug, "Integration {} is disabled", integration.id);
+                continue;
+            }
+            let specifics = integration.clone().provider_specifics.unwrap();
+            let response = match integration.provider {
+                IntegrationProvider::Audiobookshelf => {
+                    yank::audiobookshelf::sync_to_owned_collection(
+                        specifics.audiobookshelf_base_url.unwrap(),
+                        GoogleBooksService::new(&self.0.config.books.google_books).await,
+                    )
+                    .await
+                }
+                IntegrationProvider::Komga => {
+                    yank::komga::sync_to_owned_collection(
+                        specifics.komga_base_url.unwrap(),
+                        specifics.komga_username.unwrap(),
+                        specifics.komga_password.unwrap(),
+                        specifics.komga_provider.unwrap(),
+                        &self.0.db,
+                    )
+                    .await
+                }
+                IntegrationProvider::PlexYank => {
+                    yank::plex::sync_to_owned_collection(
+                        specifics.plex_yank_base_url.unwrap(),
+                        specifics.plex_yank_token.unwrap(),
+                    )
+                    .await
+                }
+                _ => continue,
+            };
+            if let Ok(update) = response {
+                progress_updates.push((integration, update));
+            }
+        }
+        for (integration, progress_updates) in progress_updates.into_iter() {
+            self.integration_progress_update(integration, progress_updates)
+                .await
+                .trace_ok();
+        }
+        Ok(true)
+    }
+
+    pub async fn sync_integrations_data_to_owned_collection(&self) -> GqlResult<()> {
+        let users_with_integrations = Integration::find()
+            .filter(integration::Column::SyncToOwnedCollection.eq(true))
+            .select_only()
+            .column(integration::Column::UserId)
+            .into_tuple::<String>()
+            .all(&self.0.db)
+            .await?
+            .into_iter()
+            .collect::<HashSet<String>>();
+        for user_id in users_with_integrations {
+            ryot_log!(
+                debug,
+                "Syncing integrations data to owned collection for user {}",
+                user_id
+            );
+            self.sync_integrations_data_to_owned_collection_for_user(&user_id)
+                .await?;
+        }
+        Ok(())
+    }
 }
diff --git a/crates/services/integration/src/push/jellyfin.rs b/crates/services/integration/src/push/jellyfin.rs
index 14b800b9c6..f7b73031d5 100644
--- a/crates/services/integration/src/push/jellyfin.rs
+++ b/crates/services/integration/src/push/jellyfin.rs
@@ -6,102 +6,75 @@ use media_models::SeenShowExtraInformation;
 use serde_json::json;
 use traits::TraceOk;
 
-pub(crate) struct JellyfinPushIntegration<'a> {
+pub async fn push_progress(
     base_url: String,
     username: String,
     password: String,
-    metadata_lot: &'a MediaLot,
-    metadata_title: &'a String,
-    show_extra_information: &'a Option<SeenShowExtraInformation>,
-}
-
-impl<'a> JellyfinPushIntegration<'a> {
-    pub const fn new(
-        base_url: String,
-        username: String,
-        password: String,
-        metadata_lot: &'a MediaLot,
-        metadata_title: &'a String,
-        show_extra_information: &'a Option<SeenShowExtraInformation>,
-    ) -> Self {
-        Self {
-            base_url,
-            username,
-            password,
-            metadata_lot,
-            metadata_title,
-            show_extra_information,
+    metadata_lot: &MediaLot,
+    metadata_title: &String,
+    show_extra_information: &Option<SeenShowExtraInformation>,
+) -> Result<()> {
+    match *metadata_lot {
+        MediaLot::Movie | MediaLot::Show => {}
+        _ => {
+            ryot_log!(
+                debug,
+                "Not pushing {:#?} progress for jellyfin push integration",
+                metadata_lot
+            );
+            return Ok(());
         }
     }
-
-    pub async fn push_progress(&self) -> Result<()> {
-        match *self.metadata_lot {
-            MediaLot::Movie | MediaLot::Show => {}
-            _ => {
-                ryot_log!(
-                    debug,
-                    "Not pushing {:#?} progress for jellyfin push integration",
-                    self.metadata_lot
-                );
-                return Ok(());
-            }
-        }
-        let (client, user_id) =
-            get_authenticated_client(&self.base_url, &self.username, &self.password).await?;
-        let json =
-            json!({ "Recursive": true, "SearchTerm": self.metadata_title, "HasTmdbId": true });
-        let items = client
-            .get(format!("{}/Users/{}/Items", &self.base_url, user_id))
-            .query(&json)
-            .send()
-            .await?
-            .json::<ItemsResponse>()
-            .await?;
-        if let Some(selected_item) = items.items.first() {
-            let id = match self.show_extra_information {
-                Some(extra_information) => {
-                    let mut return_id = None;
-                    let id = selected_item.id.clone();
-                    let season = client
-                        .get(format!("{}/Shows/{}/Seasons", self.base_url, id))
-                        .query(&json!({ "UserId": user_id }))
+    let (client, user_id) = get_authenticated_client(&base_url, &username, &password).await?;
+    let json = json!({ "Recursive": true, "SearchTerm": metadata_title, "HasTmdbId": true });
+    let items = client
+        .get(format!("{}/Users/{}/Items", base_url, user_id))
+        .query(&json)
+        .send()
+        .await?
+        .json::<ItemsResponse>()
+        .await?;
+    if let Some(selected_item) = items.items.first() {
+        let id = match show_extra_information {
+            Some(extra_information) => {
+                let mut return_id = None;
+                let id = selected_item.id.clone();
+                let season = client
+                    .get(format!("{}/Shows/{}/Seasons", base_url, id))
+                    .query(&json!({ "UserId": user_id }))
+                    .send()
+                    .await?
+                    .json::<ItemsResponse>()
+                    .await?
+                    .items
+                    .into_iter()
+                    .find(|s| s.index_number == Some(extra_information.season));
+                if let Some(season) = season {
+                    let episode = client
+                        .get(format!("{}/Shows/{}/Episodes", base_url, id))
+                        .query(&json!({ "UserId": user_id, "SeasonId": season.id }))
                         .send()
                         .await?
                         .json::<ItemsResponse>()
                         .await?
                         .items
                         .into_iter()
-                        .find(|s| s.index_number == Some(extra_information.season));
-                    if let Some(season) = season {
-                        let episode = client
-                            .get(format!("{}/Shows/{}/Episodes", self.base_url, id))
-                            .query(&json!({ "UserId": user_id, "SeasonId": season.id }))
-                            .send()
-                            .await?
-                            .json::<ItemsResponse>()
-                            .await?
-                            .items
-                            .into_iter()
-                            .find(|e| e.index_number == Some(extra_information.episode));
-                        return_id = episode.map(|e| e.id);
-                    }
-                    return_id
+                        .find(|e| e.index_number == Some(extra_information.episode));
+                    return_id = episode.map(|e| e.id);
                 }
-                None => Some(selected_item.id.clone()),
-            };
-            if let Some(id) = id {
-                client
-                    .post(format!(
-                        "{}/Users/{}/PlayedItems/{}",
-                        self.base_url, user_id, id
-                    ))
-                    .send()
-                    .await?
-                    .json::<serde_json::Value>()
-                    .await
-                    .trace_ok();
+                return_id
             }
+            None => Some(selected_item.id.clone()),
+        };
+        if let Some(id) = id {
+            client
+                .post(format!("{}/Users/{}/PlayedItems/{}", base_url, user_id, id))
+                .send()
+                .await?
+                .json::<serde_json::Value>()
+                .await
+                .trace_ok();
         }
-        Ok(())
     }
+    Ok(())
 }
diff --git a/crates/services/integration/src/push/radarr.rs b/crates/services/integration/src/push/radarr.rs
index f6d30982b3..57960f32a9 100644
--- a/crates/services/integration/src/push/radarr.rs
+++ b/crates/services/integration/src/push/radarr.rs
@@ -10,7 +10,7 @@ use radarr_api_rs::{
 };
 use traits::TraceOk;
 
-pub(crate) struct RadarrPushIntegration {
+pub async fn push_progress(
     api_key: String,
     profile_id: i32,
     tmdb_id: String,
@@ -18,53 +18,29 @@ pub(crate) struct RadarrPushIntegration {
     metadata_lot: MediaLot,
     metadata_title: String,
     root_folder_path: String,
-}
-
-impl RadarrPushIntegration {
-    pub const fn new(
-        api_key: String,
-        profile_id: i32,
-        tmdb_id: String,
-        base_url: String,
-        metadata_lot: MediaLot,
-        metadata_title: String,
-        root_folder_path: String,
-    ) -> Self {
-        Self {
-            api_key,
-            tmdb_id,
-            base_url,
-            profile_id,
-            metadata_lot,
-            metadata_title,
-            root_folder_path,
-        }
-    }
-
-    pub async fn push_progress(&self) -> Result<()> {
-        if self.metadata_lot != MediaLot::Movie {
-            ryot_log!(debug, "Not a movie, skipping {:#?}", self.metadata_title);
-            return Ok(());
-        }
-        let mut configuration = RadarrConfiguration::new();
-        configuration.base_path = self.base_url.clone();
-        configuration.api_key = Some(RadarrApiKey {
-            key: self.api_key.clone(),
-            prefix: None,
-        });
-        let mut resource = RadarrMovieResource::new();
-        resource.tmdb_id = Some(self.tmdb_id.parse().unwrap());
-        resource.quality_profile_id = Some(self.profile_id);
-        resource.root_folder_path = Some(Some(self.root_folder_path.clone()));
-        resource.monitored = Some(true);
-        resource.title = Some(Some(self.metadata_title.clone()));
-        let mut options = RadarrAddMovieOptions::new();
-        options.search_for_movie = Some(true);
-        resource.add_options = Some(Box::new(options));
-        ryot_log!(debug, "Pushing movie to Radarr {:?}", resource);
-        radarr_api_v3_movie_post(&configuration, Some(resource))
-            .await
-            .trace_ok();
-        Ok(())
+) -> Result<()> {
+    if metadata_lot != MediaLot::Movie {
+        ryot_log!(debug, "Not a movie, skipping {:#?}", metadata_title);
+        return Ok(());
     }
+    let mut configuration = RadarrConfiguration::new();
+    configuration.base_path = base_url.clone();
+    configuration.api_key = Some(RadarrApiKey {
+        prefix: None,
+        key: api_key.clone(),
+    });
+    let mut resource = RadarrMovieResource::new();
+    resource.tmdb_id = Some(tmdb_id.parse().unwrap());
+    resource.quality_profile_id = Some(profile_id);
+    resource.root_folder_path = Some(Some(root_folder_path.clone()));
+    resource.monitored = Some(true);
+    resource.title = Some(Some(metadata_title.clone()));
+    let mut options = RadarrAddMovieOptions::new();
+    options.search_for_movie = Some(true);
+    resource.add_options = Some(Box::new(options));
+    ryot_log!(debug, "Pushing movie to Radarr {:?}", resource);
+    radarr_api_v3_movie_post(&configuration, Some(resource))
+        .await
+        .trace_ok();
+    Ok(())
 }
diff --git a/crates/services/integration/src/push/sonarr.rs b/crates/services/integration/src/push/sonarr.rs
index 626cbad63d..e77a29a6f2 100644
--- a/crates/services/integration/src/push/sonarr.rs
+++ b/crates/services/integration/src/push/sonarr.rs
@@ -10,7 +10,7 @@ use sonarr_api_rs::{
 };
 use traits::TraceOk;
 
-pub(crate) struct SonarrPushIntegration {
+pub async fn push_progress(
     api_key: String,
     profile_id: i32,
     tvdb_id: String,
@@ -18,55 +18,31 @@ pub(crate) struct SonarrPushIntegration {
     metadata_lot: MediaLot,
     metadata_title: String,
     root_folder_path: String,
-}
-
-impl SonarrPushIntegration {
-    pub const fn new(
-        api_key: String,
-        profile_id: i32,
-        tvdb_id: String,
-        base_url: String,
-        metadata_lot: MediaLot,
-        metadata_title: String,
-        root_folder_path: String,
-    ) -> Self {
-        Self {
-            tvdb_id,
-            api_key,
-            base_url,
-            profile_id,
-            metadata_lot,
-            metadata_title,
-            root_folder_path,
-        }
-    }
-
-    pub async fn push_progress(&self) -> Result<()> {
-        if self.metadata_lot != MediaLot::Show {
-            ryot_log!(debug, "Not a show, skipping {:#?}", self.metadata_title);
-            return Ok(());
-        }
-        let mut configuration = SonarrConfiguration::new();
-        configuration.base_path = self.base_url.clone();
-        configuration.api_key = Some(SonarrApiKey {
-            key: self.api_key.clone(),
-            prefix: None,
-        });
-        let mut resource = SonarrSeriesResource::new();
-        resource.title = Some(Some(self.tvdb_id.clone()));
-        resource.tvdb_id = Some(self.tvdb_id.parse().unwrap());
-        resource.quality_profile_id = Some(self.profile_id);
-        resource.root_folder_path = Some(Some(self.root_folder_path.clone()));
-        resource.monitored = Some(true);
-        resource.season_folder = Some(true);
-        resource.title = Some(Some(self.metadata_title.clone()));
-        let mut options = SonarrAddSeriesOptions::new();
-        options.search_for_missing_episodes = Some(true);
-        resource.add_options = Some(Box::new(options));
-        ryot_log!(debug, "Pushing series to Sonarr {:?}", resource);
-        sonarr_api_v3_series_post(&configuration, Some(resource))
-            .await
-            .trace_ok();
-        Ok(())
+) -> Result<()> {
+    if metadata_lot != MediaLot::Show {
+        ryot_log!(debug, "Not a show, skipping {:#?}", metadata_title);
+        return Ok(());
     }
+    let mut configuration = SonarrConfiguration::new();
+    configuration.base_path = base_url.clone();
+    configuration.api_key = Some(SonarrApiKey {
+        prefix: None,
+        key: api_key.clone(),
+    });
+    let mut resource = SonarrSeriesResource::new();
+    resource.title = Some(Some(tvdb_id.clone()));
+    resource.tvdb_id = Some(tvdb_id.parse().unwrap());
+    resource.quality_profile_id = Some(profile_id);
+    resource.root_folder_path = Some(Some(root_folder_path.clone()));
+    resource.monitored = Some(true);
+    resource.season_folder = Some(true);
+    resource.title = Some(Some(metadata_title.clone()));
+    let mut options = SonarrAddSeriesOptions::new();
+    options.search_for_missing_episodes = Some(true);
+    resource.add_options = Some(Box::new(options));
+    ryot_log!(debug, "Pushing series to Sonarr {:?}", resource);
+    sonarr_api_v3_series_post(&configuration, Some(resource))
+        .await
+        .trace_ok();
+    Ok(())
 }
diff --git a/crates/services/integration/src/sink/emby.rs b/crates/services/integration/src/sink/emby.rs
index 006c78bd35..f5da9fe6c0 100644
--- a/crates/services/integration/src/sink/emby.rs
+++ b/crates/services/integration/src/sink/emby.rs
@@ -47,63 +47,55 @@ mod models {
     }
 }
 
-pub(crate) struct EmbySinkIntegration {
-    payload: String,
-    db: DatabaseConnection,
-}
-
-impl EmbySinkIntegration {
-    pub const fn new(payload: String, db: DatabaseConnection) -> Self {
-        Self { payload, db }
-    }
-
-    pub async fn yank_progress(&self) -> Result<ImportResult> {
-        let payload: models::EmbyWebhookPayload = serde_json::from_str(&self.payload)?;
-        let runtime = payload
-            .item
-            .run_time_ticks
-            .ok_or_else(|| anyhow::anyhow!("No run time associated with this media"))?;
-        let position = payload
-            .playback_info
-            .position_ticks
-            .ok_or_else(|| anyhow::anyhow!("No position associated with this media"))?;
-        let (identifier, lot) =
-            match payload.item.item_type.as_str() {
-                "Movie" => {
-                    let id =
-                        payload.item.provider_ids.tmdb.as_ref().ok_or_else(|| {
-                            anyhow::anyhow!("No TMDb ID associated with this media")
-                        })?;
-                    (id.clone(), MediaLot::Movie)
-                }
-                "Episode" => {
-                    let series_name = payload.item.series_name.as_ref().ok_or_else(|| {
+pub async fn yank_progress(payload: String, db: &DatabaseConnection) -> Result<ImportResult> {
+    let payload: models::EmbyWebhookPayload = serde_json::from_str(&payload)?;
+    let runtime = payload
+        .item
+        .run_time_ticks
+        .ok_or_else(|| anyhow::anyhow!("No run time associated with this media"))?;
+    let position = payload
+        .playback_info
+        .position_ticks
+        .ok_or_else(|| anyhow::anyhow!("No position associated with this media"))?;
+    let (identifier, lot) =
+        match payload.item.item_type.as_str() {
+            "Movie" => {
+                let id = payload
+                    .item
+                    .provider_ids
+                    .tmdb
+                    .as_ref()
+                    .ok_or_else(|| anyhow::anyhow!("No TMDb ID associated with this media"))?;
+                (id.clone(), MediaLot::Movie)
+            }
+            "Episode" => {
+                let series_name =
+                    payload.item.series_name.as_ref().ok_or_else(|| {
                         anyhow::anyhow!("No series name associated with this media")
                     })?;
-                    let episode_name = payload.item.episode_name.as_ref().ok_or_else(|| {
+                let episode_name =
+                    payload.item.episode_name.as_ref().ok_or_else(|| {
                         anyhow::anyhow!("No episode name associated with this media")
                     })?;
-                    let db_show =
-                        get_show_by_episode_identifier(&self.db, series_name, episode_name).await?;
-                    (db_show.identifier, MediaLot::Show)
-                }
-                _ => bail!("Only movies and shows supported"),
-            };
-        Ok(ImportResult {
-            completed: vec![ImportCompletedItem::Metadata(ImportOrExportMetadataItem {
-                lot,
-                identifier,
-                source: MediaSource::Tmdb,
-                seen_history: vec![ImportOrExportMetadataItemSeen {
-                    provider_watched_on: Some("Emby".to_string()),
-                    progress: Some(position / runtime * dec!(100)),
-                    show_season_number: payload.item.season_number,
-                    show_episode_number: payload.item.episode_number,
-                    ..Default::default()
-                }],
+                let db_show = get_show_by_episode_identifier(db, series_name, episode_name).await?;
+                (db_show.identifier, MediaLot::Show)
+            }
+            _ => bail!("Only movies and shows supported"),
+        };
+    Ok(ImportResult {
+        completed: vec![ImportCompletedItem::Metadata(ImportOrExportMetadataItem {
+            lot,
+            identifier,
+            source: MediaSource::Tmdb,
+            seen_history: vec![ImportOrExportMetadataItemSeen {
+                provider_watched_on: Some("Emby".to_string()),
+                progress: Some(position / runtime * dec!(100)),
+                show_season_number: payload.item.season_number,
+                show_episode_number: payload.item.episode_number,
                 ..Default::default()
-            })],
+            }],
             ..Default::default()
-        })
-    }
+        })],
+        ..Default::default()
+    })
 }
diff --git a/crates/services/integration/src/sink/generic_json.rs b/crates/services/integration/src/sink/generic_json.rs
index f73274e3fd..98b0d95fe1 100644
--- a/crates/services/integration/src/sink/generic_json.rs
+++ b/crates/services/integration/src/sink/generic_json.rs
@@ -1,38 +1,29 @@
 use anyhow::{bail, Result};
 use dependent_models::{CompleteExport, ImportCompletedItem, ImportResult};
 
-pub(crate) struct GenericJsonSinkIntegration {
-    payload: String,
-}
-impl GenericJsonSinkIntegration {
-    pub const fn new(payload: String) -> Self {
-        Self { payload }
+pub async fn yank_progress(payload: String) -> Result<ImportResult> {
+    let payload = match serde_json::from_str::<CompleteExport>(&payload) {
+        Ok(val) => val,
+        Err(err) => bail!(err),
+    };
+    let mut completed = vec![];
+    for media in payload.media.unwrap_or_default() {
+        completed.push(ImportCompletedItem::Metadata(media));
     }
-
-    pub async fn yank_progress(&self) -> Result<ImportResult> {
-        let payload = match serde_json::from_str::<CompleteExport>(&self.payload) {
-            Ok(val) => val,
-            Err(err) => bail!(err),
-        };
-        let mut completed = vec![];
-        for media in payload.media.unwrap_or_default() {
-            completed.push(ImportCompletedItem::Metadata(media));
-        }
-        for people in payload.people.unwrap_or_default() {
-            completed.push(ImportCompletedItem::Person(people));
-        }
-        for measurement in payload.measurements.unwrap_or_default() {
-            completed.push(ImportCompletedItem::Measurement(measurement));
-        }
-        for workout in payload.workouts.unwrap_or_default() {
-            completed.push(ImportCompletedItem::ApplicationWorkout(workout));
-        }
-        for media_group in payload.media_groups.unwrap_or_default() {
-            completed.push(ImportCompletedItem::MetadataGroup(media_group));
-        }
-        Ok(ImportResult {
-            completed,
-            ..Default::default()
-        })
+    for people in payload.people.unwrap_or_default() {
+        completed.push(ImportCompletedItem::Person(people));
+    }
+    for measurement in payload.measurements.unwrap_or_default() {
+        completed.push(ImportCompletedItem::Measurement(measurement));
+    }
+    for workout in payload.workouts.unwrap_or_default() {
+        completed.push(ImportCompletedItem::ApplicationWorkout(workout));
+    }
+    for media_group in payload.media_groups.unwrap_or_default() {
+        completed.push(ImportCompletedItem::MetadataGroup(media_group));
     }
+    Ok(ImportResult {
+        completed,
+        ..Default::default()
+    })
 }
diff --git a/crates/services/integration/src/sink/jellyfin.rs b/crates/services/integration/src/sink/jellyfin.rs
index fef7743c7f..182fbb5a75 100644
--- a/crates/services/integration/src/sink/jellyfin.rs
+++ b/crates/services/integration/src/sink/jellyfin.rs
@@ -46,63 +46,53 @@ mod models {
     }
 }
 
-pub(crate) struct JellyfinSinkIntegration {
-    payload: String,
-}
-
-impl JellyfinSinkIntegration {
-    pub const fn new(payload: String) -> Self {
-        Self { payload }
-    }
-
-    pub async fn yank_progress(&self) -> Result<ImportResult> {
-        let payload = serde_json::from_str::<models::JellyfinWebhookPayload>(&self.payload)?;
-        let identifier = payload
-            .item
-            .provider_ids
-            .tmdb
-            .as_ref()
-            .or_else(|| {
-                payload
-                    .series
-                    .as_ref()
-                    .and_then(|s| s.provider_ids.tmdb.as_ref())
-            })
-            .ok_or_else(|| anyhow::anyhow!("No TMDb ID associated with this media"))?
-            .clone();
+pub async fn yank_progress(payload: String) -> Result<ImportResult> {
+    let payload = serde_json::from_str::<models::JellyfinWebhookPayload>(&payload)?;
+    let identifier = payload
+        .item
+        .provider_ids
+        .tmdb
+        .as_ref()
+        .or_else(|| {
+            payload
+                .series
+                .as_ref()
+                .and_then(|s| s.provider_ids.tmdb.as_ref())
+        })
+        .ok_or_else(|| anyhow::anyhow!("No TMDb ID associated with this media"))?
+        .clone();
 
-        let runtime = payload
-            .item
-            .run_time_ticks
-            .ok_or_else(|| anyhow::anyhow!("No run time associated with this media"))?;
+    let runtime = payload
+        .item
+        .run_time_ticks
+        .ok_or_else(|| anyhow::anyhow!("No run time associated with this media"))?;
 
-        let position = payload
-            .session
-            .play_state
-            .position_ticks
-            .ok_or_else(|| anyhow::anyhow!("No position associated with this media"))?;
+    let position = payload
+        .session
+        .play_state
+        .position_ticks
+        .ok_or_else(|| anyhow::anyhow!("No position associated with this media"))?;
 
-        let lot = match payload.item.item_type.as_str() {
-            "Episode" => MediaLot::Show,
-            "Movie" => MediaLot::Movie,
-            _ => bail!("Only movies and shows supported"),
-        };
+    let lot = match payload.item.item_type.as_str() {
+        "Episode" => MediaLot::Show,
+        "Movie" => MediaLot::Movie,
+        _ => bail!("Only movies and shows supported"),
+    };
 
-        Ok(ImportResult {
-            completed: vec![ImportCompletedItem::Metadata(ImportOrExportMetadataItem {
-                lot,
-                identifier,
-                source: MediaSource::Tmdb,
-                seen_history: vec![ImportOrExportMetadataItemSeen {
-                    progress: Some(position / runtime * dec!(100)),
-                    show_season_number: payload.item.season_number,
-                    show_episode_number: payload.item.episode_number,
-                    provider_watched_on: Some("Jellyfin".to_string()),
-                    ..Default::default()
-                }],
+    Ok(ImportResult {
+        completed: vec![ImportCompletedItem::Metadata(ImportOrExportMetadataItem {
+            lot,
+            identifier,
+            source: MediaSource::Tmdb,
+            seen_history: vec![ImportOrExportMetadataItemSeen {
+                progress: Some(position / runtime * dec!(100)),
+                show_season_number: payload.item.season_number,
+                show_episode_number: payload.item.episode_number,
+                provider_watched_on: Some("Jellyfin".to_string()),
                 ..Default::default()
-            })],
+            }],
             ..Default::default()
-        })
-    }
+        })],
+        ..Default::default()
+    })
 }
diff --git a/crates/services/integration/src/sink/kodi.rs b/crates/services/integration/src/sink/kodi.rs
index ffc3afb33f..1bbe987b61 100644
--- a/crates/services/integration/src/sink/kodi.rs
+++ b/crates/services/integration/src/sink/kodi.rs
@@ -14,35 +14,26 @@ struct IntegrationMediaSeen {
     show_episode_number: Option<i32>,
 }
 
-pub(crate) struct KodiSinkIntegration {
-    payload: String,
-}
-impl KodiSinkIntegration {
-    pub const fn new(payload: String) -> Self {
-        Self { payload }
-    }
-
-    pub async fn yank_progress(&self) -> Result<ImportResult> {
-        let payload = match serde_json::from_str::<IntegrationMediaSeen>(&self.payload) {
-            Ok(val) => val,
-            Err(err) => bail!(err),
-        };
+pub async fn yank_progress(payload: String) -> Result<ImportResult> {
+    let payload = match serde_json::from_str::<IntegrationMediaSeen>(&payload) {
+        Ok(val) => val,
+        Err(err) => bail!(err),
+    };
 
-        Ok(ImportResult {
-            completed: vec![ImportCompletedItem::Metadata(ImportOrExportMetadataItem {
-                lot: payload.lot,
-                source: MediaSource::Tmdb,
-                identifier: payload.identifier,
-                seen_history: vec![ImportOrExportMetadataItemSeen {
-                    progress: Some(payload.progress),
-                    provider_watched_on: Some("Kodi".to_string()),
-                    show_season_number: payload.show_season_number,
-                    show_episode_number: payload.show_episode_number,
-                    ..Default::default()
-                }],
+    Ok(ImportResult {
+        completed: vec![ImportCompletedItem::Metadata(ImportOrExportMetadataItem {
+            lot: payload.lot,
+            source: MediaSource::Tmdb,
+            identifier: payload.identifier,
+            seen_history: vec![ImportOrExportMetadataItemSeen {
+                progress: Some(payload.progress),
+                provider_watched_on: Some("Kodi".to_string()),
+                show_season_number: payload.show_season_number,
+                show_episode_number: payload.show_episode_number,
                 ..Default::default()
-            })],
+            }],
             ..Default::default()
-        })
-    }
+        })],
+        ..Default::default()
+    })
 }
diff --git a/crates/services/integration/src/sink/plex.rs b/crates/services/integration/src/sink/plex.rs
index 1eba48b6af..6b125de088 100644
--- a/crates/services/integration/src/sink/plex.rs
+++ b/crates/services/integration/src/sink/plex.rs
@@ -48,99 +48,86 @@ mod models {
     }
 }
 
-pub(crate) struct PlexSinkIntegration {
-    payload: String,
-    db: DatabaseConnection,
-    plex_user: Option<String>,
+fn parse_payload(payload: &str) -> Result<models::PlexWebhookPayload> {
+    let payload_regex = Regex::new(r"\{.*\}").unwrap();
+    let json_payload = payload_regex
+        .find(payload)
+        .map(|x| x.as_str())
+        .unwrap_or("");
+    serde_json::from_str(json_payload).context("Error during JSON payload deserialization")
 }
 
-impl PlexSinkIntegration {
-    pub const fn new(payload: String, plex_user: Option<String>, db: DatabaseConnection) -> Self {
-        Self {
-            db,
-            payload,
-            plex_user,
-        }
-    }
-
-    fn parse_payload(&self, payload: &str) -> Result<models::PlexWebhookPayload> {
-        let payload_regex = Regex::new(r"\{.*\}").unwrap();
-        let json_payload = payload_regex
-            .find(payload)
-            .map(|x| x.as_str())
-            .unwrap_or("");
-        serde_json::from_str(json_payload).context("Error during JSON payload deserialization")
-    }
-
-    fn get_tmdb_identifier<'a>(&self, guids: &'a [StringIdObject]) -> Result<&'a str> {
-        guids
-            .iter()
-            .find(|g| g.id.starts_with("tmdb://"))
-            .map(|g| &g.id[7..])
-            .ok_or_else(|| anyhow::anyhow!("No TMDb ID associated with this media"))
-    }
+fn get_tmdb_identifier(guids: &[StringIdObject]) -> Result<&str> {
+    guids
+        .iter()
+        .find(|g| g.id.starts_with("tmdb://"))
+        .map(|g| &g.id[7..])
+        .ok_or_else(|| anyhow::anyhow!("No TMDb ID associated with this media"))
+}
 
-    async fn get_media_info<'a>(
-        &self,
-        metadata: &'a models::PlexWebhookMetadataPayload,
-        identifier: &'a str,
-    ) -> Result<(String, MediaLot)> {
-        match metadata.item_type.as_str() {
-            "movie" => Ok((identifier.to_owned(), MediaLot::Movie)),
-            "episode" => {
-                let series_name = metadata.show_name.as_ref().context("Show name missing")?;
-                let db_show =
-                    get_show_by_episode_identifier(&self.db, series_name, identifier).await?;
-                Ok((db_show.identifier, MediaLot::Show))
-            }
-            _ => bail!("Only movies and shows supported"),
+async fn get_media_info<'a>(
+    db: &DatabaseConnection,
+    metadata: &'a models::PlexWebhookMetadataPayload,
+    identifier: &'a str,
+) -> Result<(String, MediaLot)> {
+    match metadata.item_type.as_str() {
+        "movie" => Ok((identifier.to_owned(), MediaLot::Movie)),
+        "episode" => {
+            let series_name = metadata.show_name.as_ref().context("Show name missing")?;
+            let db_show = get_show_by_episode_identifier(db, series_name, identifier).await?;
+            Ok((db_show.identifier, MediaLot::Show))
         }
+        _ => bail!("Only movies and shows supported"),
     }
+}
 
-    fn calculate_progress(&self, payload: &models::PlexWebhookPayload) -> Result<Decimal> {
-        match payload.metadata.view_offset {
-            Some(offset) => Ok(offset / payload.metadata.duration * dec!(100)),
-            None if payload.event_type == "media.scrobble" => Ok(dec!(100)),
-            None => bail!("No position associated with this media"),
-        }
+fn calculate_progress(payload: &models::PlexWebhookPayload) -> Result<Decimal> {
+    match payload.metadata.view_offset {
+        Some(offset) => Ok(offset / payload.metadata.duration * dec!(100)),
+        None if payload.event_type == "media.scrobble" => Ok(dec!(100)),
+        None => bail!("No position associated with this media"),
     }
+}
 
-    pub async fn yank_progress(&self) -> Result<ImportResult> {
-        let payload = self.parse_payload(&self.payload)?;
+pub async fn yank_progress(
+    payload: String,
+    db: &DatabaseConnection,
+    plex_user: Option<String>,
+) -> Result<ImportResult> {
+    let payload = parse_payload(&payload)?;
 
-        if let Some(plex_user) = &self.plex_user {
-            if *plex_user != payload.account.plex_user {
-                bail!(
-                    "Ignoring non matching user {:#?}",
-                    payload.account.plex_user
-                );
-            }
+    if let Some(plex_user) = &plex_user {
+        if *plex_user != payload.account.plex_user {
+            bail!(
+                "Ignoring non matching user {:#?}",
+                payload.account.plex_user
+            );
         }
+    }
 
-        match payload.event_type.as_str() {
-            "media.scrobble" | "media.play" | "media.pause" | "media.resume" | "media.stop" => {}
-            _ => bail!("Ignoring event type {:#?}", payload.event_type),
-        };
+    match payload.event_type.as_str() {
+        "media.scrobble" | "media.play" | "media.pause" | "media.resume" | "media.stop" => {}
+        _ => bail!("Ignoring event type {:#?}", payload.event_type),
+    };
 
-        let identifier = self.get_tmdb_identifier(&payload.metadata.guids)?;
-        let (identifier, lot) = self.get_media_info(&payload.metadata, identifier).await?;
-        let progress = self.calculate_progress(&payload)?;
+    let identifier = get_tmdb_identifier(&payload.metadata.guids)?;
+    let (identifier, lot) = get_media_info(db, &payload.metadata, identifier).await?;
+    let progress = calculate_progress(&payload)?;
 
-        Ok(ImportResult {
-            completed: vec![ImportCompletedItem::Metadata(ImportOrExportMetadataItem {
-                lot,
-                identifier,
-                source: MediaSource::Tmdb,
-                seen_history: vec![ImportOrExportMetadataItemSeen {
-                    progress: Some(progress),
-                    provider_watched_on: Some("Plex".to_string()),
-                    show_season_number: payload.metadata.season_number,
-                    show_episode_number: payload.metadata.episode_number,
-                    ..Default::default()
-                }],
+    Ok(ImportResult {
+        completed: vec![ImportCompletedItem::Metadata(ImportOrExportMetadataItem {
+            lot,
+            identifier,
+            source: MediaSource::Tmdb,
+            seen_history: vec![ImportOrExportMetadataItemSeen {
+                progress: Some(progress),
+                provider_watched_on: Some("Plex".to_string()),
+                show_season_number: payload.metadata.season_number,
+                show_episode_number: payload.metadata.episode_number,
                 ..Default::default()
-            })],
+            }],
             ..Default::default()
-        })
-    }
+        })],
+        ..Default::default()
+    })
 }
diff --git a/crates/services/integration/src/yank/audiobookshelf.rs b/crates/services/integration/src/yank/audiobookshelf.rs
index fa82f0ae9b..0fa7531bd3 100644
--- a/crates/services/integration/src/yank/audiobookshelf.rs
+++ b/crates/services/integration/src/yank/audiobookshelf.rs
@@ -10,226 +10,217 @@ use dependent_models::{ImportCompletedItem, ImportResult};
 use enums::{MediaLot, MediaSource};
 use media_models::{CommitMediaInput, ImportOrExportMetadataItem, ImportOrExportMetadataItemSeen};
 use providers::google_books::GoogleBooksService;
-use reqwest::header::{HeaderValue, AUTHORIZATION};
+use reqwest::{
+    header::{HeaderValue, AUTHORIZATION},
+    Client,
+};
 use rust_decimal_macros::dec;
 use specific_models::audiobookshelf::{self, LibrariesListResponse, ListResponse};
 
-pub(crate) struct AudiobookshelfYankIntegration {
+fn get_http_client(access_token: &String) -> Client {
+    get_base_http_client(Some(vec![(
+        AUTHORIZATION,
+        HeaderValue::from_str(&format!("Bearer {}", access_token)).unwrap(),
+    )]))
+}
+
+pub async fn yank_progress<F>(
     base_url: String,
     access_token: String,
-    sync_to_owned_collection: Option<bool>,
+    // TODO: Find a way to use `get_identifier_from_book_isbn` function
     isbn_service: GoogleBooksService,
-}
+    commit_metadata: impl Fn(CommitMediaInput) -> F,
+) -> Result<ImportResult>
+where
+    F: Future<Output = GqlResult<metadata::Model>>,
+{
+    let url = format!("{}/api", base_url);
+    let client = get_http_client(&access_token);
 
-impl AudiobookshelfYankIntegration {
-    pub fn new(
-        base_url: String,
-        access_token: String,
-        sync_to_owned_collection: Option<bool>,
-        isbn_service: GoogleBooksService,
-    ) -> Self {
-        Self {
-            base_url,
-            access_token,
-            sync_to_owned_collection,
-            isbn_service,
-        }
-    }
+    let resp = client
+        .get(format!("{}/me/items-in-progress", url))
+        .send()
+        .await
+        .map_err(|e| anyhow!(e))?
+        .json::<audiobookshelf::Response>()
+        .await
+        .map_err(|e| anyhow!(e))?;
 
-    pub async fn yank_progress<F>(
-        &self,
-        commit_metadata: impl Fn(CommitMediaInput) -> F,
-    ) -> Result<ImportResult>
-    where
-        F: Future<Output = GqlResult<metadata::Model>>,
-    {
-        let url = format!("{}/api", self.base_url);
-        let client = get_base_http_client(Some(vec![(
-            AUTHORIZATION,
-            HeaderValue::from_str(&format!("Bearer {}", self.access_token)).unwrap(),
-        )]));
+    ryot_log!(debug, "Got response for items in progress {:?}", resp);
 
-        let resp = client
-            .get(format!("{}/me/items-in-progress", url))
+    let mut result = ImportResult::default();
+
+    for item in resp.library_items.iter() {
+        let metadata = item.media.clone().unwrap().metadata;
+        let (progress_id, identifier, lot, source, podcast_episode_number) =
+            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) => (
+                            item.id.clone(),
+                            id,
+                            MediaLot::Book,
+                            MediaSource::GoogleBooks,
+                            None,
+                        ),
+                        _ => {
+                            ryot_log!(debug, "No Google Books ID found for ISBN {:#?}", isbn);
+                            continue;
+                        }
+                    },
+                    _ => {
+                        ryot_log!(debug, "No ISBN found for item {:#?}", item);
+                        continue;
+                    }
+                }
+            } else if let Some(asin) = metadata.asin.clone() {
+                (
+                    item.id.clone(),
+                    asin,
+                    MediaLot::AudioBook,
+                    MediaSource::Audible,
+                    None,
+                )
+            } else if let Some(itunes_id) = metadata.itunes_id.clone() {
+                match &item.recent_episode {
+                    Some(pe) => {
+                        let lot = MediaLot::Podcast;
+                        let source = MediaSource::Itunes;
+                        let podcast = commit_metadata(CommitMediaInput {
+                            identifier: itunes_id.clone(),
+                            lot,
+                            source,
+                            ..Default::default()
+                        })
+                        .await
+                        .unwrap();
+                        match podcast
+                            .podcast_specifics
+                            .and_then(|p| get_podcast_episode_number_by_name(&p, &pe.title))
+                        {
+                            Some(episode) => (
+                                format!("{}/{}", item.id, pe.id),
+                                itunes_id,
+                                lot,
+                                source,
+                                Some(episode),
+                            ),
+                            _ => {
+                                ryot_log!(debug, "No podcast found for iTunes ID {:#?}", itunes_id);
+                                continue;
+                            }
+                        }
+                    }
+                    _ => {
+                        ryot_log!(debug, "No recent episode found for item {:?}", item);
+                        continue;
+                    }
+                }
+            } else {
+                ryot_log!(
+                    debug,
+                    "No ASIN, ISBN or iTunes ID found for item {:?}",
+                    item
+                );
+                continue;
+            };
+
+        match client
+            .get(format!("{}/me/progress/{}", url, progress_id))
             .send()
             .await
             .map_err(|e| anyhow!(e))?
-            .json::<audiobookshelf::Response>()
+            .json::<audiobookshelf::ItemProgress>()
             .await
-            .map_err(|e| anyhow!(e))?;
-
-        ryot_log!(debug, "Got response for items in progress {:?}", resp);
+            .map_err(|e| anyhow!(e))
+        {
+            Ok(resp) => {
+                ryot_log!(
+                    debug,
+                    "Got response for individual item progress {:?}",
+                    resp
+                );
+                let progress = if let Some(ebook_progress) = resp.ebook_progress {
+                    ebook_progress
+                } else {
+                    resp.progress
+                };
+                result
+                    .completed
+                    .push(ImportCompletedItem::Metadata(ImportOrExportMetadataItem {
+                        lot,
+                        source,
+                        identifier,
+                        seen_history: vec![ImportOrExportMetadataItemSeen {
+                            podcast_episode_number,
+                            progress: Some(progress * dec!(100)),
+                            provider_watched_on: Some("Audiobookshelf".to_string()),
+                            ..Default::default()
+                        }],
+                        ..Default::default()
+                    }));
+            }
+            Err(e) => {
+                ryot_log!(debug, "Error getting progress for item {:?}: {:?}", item, e);
+                continue;
+            }
+        };
+    }
+    Ok(result)
+}
 
-        let mut result = ImportResult::default();
+pub async fn sync_to_owned_collection(
+    access_token: String,
+    isbn_service: GoogleBooksService,
+) -> Result<ImportResult> {
+    let client = get_http_client(&access_token);
 
-        for item in resp.library_items.iter() {
+    let mut result = ImportResult::default();
+    let libraries_resp = client
+        .get("libraries")
+        .send()
+        .await
+        .map_err(|e| anyhow!(e))?
+        .json::<LibrariesListResponse>()
+        .await
+        .unwrap();
+    for library in libraries_resp.libraries {
+        let items = client
+            .get(format!("libraries/{}/items", library.id))
+            .send()
+            .await
+            .map_err(|e| anyhow!(e))?
+            .json::<ListResponse>()
+            .await
+            .unwrap();
+        for item in items.results.into_iter() {
             let metadata = item.media.clone().unwrap().metadata;
-            let (progress_id, identifier, lot, source, podcast_episode_number) =
+            let (identifier, lot, source) =
                 if Some("epub".to_string()) == item.media.as_ref().unwrap().ebook_format {
                     match &metadata.isbn {
-                        Some(isbn) => match self.isbn_service.id_from_isbn(isbn).await {
-                            Some(id) => (
-                                item.id.clone(),
-                                id,
-                                MediaLot::Book,
-                                MediaSource::GoogleBooks,
-                                None,
-                            ),
-                            _ => {
-                                ryot_log!(debug, "No Google Books ID found for ISBN {:#?}", isbn);
-                                continue;
-                            }
+                        Some(isbn) => match isbn_service.id_from_isbn(isbn).await {
+                            Some(id) => (id, MediaLot::Book, MediaSource::GoogleBooks),
+                            _ => continue,
                         },
-                        _ => {
-                            ryot_log!(debug, "No ISBN found for item {:#?}", item);
-                            continue;
-                        }
+                        _ => continue,
                     }
                 } else if let Some(asin) = metadata.asin.clone() {
-                    (
-                        item.id.clone(),
-                        asin,
-                        MediaLot::AudioBook,
-                        MediaSource::Audible,
-                        None,
-                    )
+                    (asin, MediaLot::AudioBook, MediaSource::Audible)
                 } else if let Some(itunes_id) = metadata.itunes_id.clone() {
-                    match &item.recent_episode {
-                        Some(pe) => {
-                            let lot = MediaLot::Podcast;
-                            let source = MediaSource::Itunes;
-                            let podcast = commit_metadata(CommitMediaInput {
-                                identifier: itunes_id.clone(),
-                                lot,
-                                source,
-                                ..Default::default()
-                            })
-                            .await
-                            .unwrap();
-                            match podcast
-                                .podcast_specifics
-                                .and_then(|p| get_podcast_episode_number_by_name(&p, &pe.title))
-                            {
-                                Some(episode) => (
-                                    format!("{}/{}", item.id, pe.id),
-                                    itunes_id,
-                                    lot,
-                                    source,
-                                    Some(episode),
-                                ),
-                                _ => {
-                                    ryot_log!(
-                                        debug,
-                                        "No podcast found for iTunes ID {:#?}",
-                                        itunes_id
-                                    );
-                                    continue;
-                                }
-                            }
-                        }
-                        _ => {
-                            ryot_log!(debug, "No recent episode found for item {:?}", item);
-                            continue;
-                        }
-                    }
+                    (itunes_id, MediaLot::Podcast, MediaSource::Itunes)
                 } else {
-                    ryot_log!(
-                        debug,
-                        "No ASIN, ISBN or iTunes ID found for item {:?}",
-                        item
-                    );
                     continue;
                 };
-
-            match client
-                .get(format!("{}/me/progress/{}", url, progress_id))
-                .send()
-                .await
-                .map_err(|e| anyhow!(e))?
-                .json::<audiobookshelf::ItemProgress>()
-                .await
-                .map_err(|e| anyhow!(e))
-            {
-                Ok(resp) => {
-                    ryot_log!(
-                        debug,
-                        "Got response for individual item progress {:?}",
-                        resp
-                    );
-                    let progress = if let Some(ebook_progress) = resp.ebook_progress {
-                        ebook_progress
-                    } else {
-                        resp.progress
-                    };
-                    result.completed.push(ImportCompletedItem::Metadata(
-                        ImportOrExportMetadataItem {
-                            lot,
-                            source,
-                            identifier,
-                            seen_history: vec![ImportOrExportMetadataItemSeen {
-                                podcast_episode_number,
-                                progress: Some(progress * dec!(100)),
-                                provider_watched_on: Some("Audiobookshelf".to_string()),
-                                ..Default::default()
-                            }],
-                            ..Default::default()
-                        },
-                    ));
-                }
-                Err(e) => {
-                    ryot_log!(debug, "Error getting progress for item {:?}: {:?}", item, e);
-                    continue;
-                }
-            };
-        }
-        if let Some(true) = self.sync_to_owned_collection {
-            let libraries_resp = client
-                .get("libraries")
-                .send()
-                .await
-                .map_err(|e| anyhow!(e))?
-                .json::<LibrariesListResponse>()
-                .await
-                .unwrap();
-            for library in libraries_resp.libraries {
-                let items = client
-                    .get(format!("libraries/{}/items", library.id))
-                    .send()
-                    .await
-                    .map_err(|e| anyhow!(e))?
-                    .json::<ListResponse>()
-                    .await
-                    .unwrap();
-                for item in items.results.into_iter() {
-                    let metadata = item.media.clone().unwrap().metadata;
-                    let (identifier, lot, source) =
-                        if Some("epub".to_string()) == item.media.as_ref().unwrap().ebook_format {
-                            match &metadata.isbn {
-                                Some(isbn) => match self.isbn_service.id_from_isbn(isbn).await {
-                                    Some(id) => (id, MediaLot::Book, MediaSource::GoogleBooks),
-                                    _ => continue,
-                                },
-                                _ => continue,
-                            }
-                        } else if let Some(asin) = metadata.asin.clone() {
-                            (asin, MediaLot::AudioBook, MediaSource::Audible)
-                        } else if let Some(itunes_id) = metadata.itunes_id.clone() {
-                            (itunes_id, MediaLot::Podcast, MediaSource::Itunes)
-                        } else {
-                            continue;
-                        };
-                    result.completed.push(ImportCompletedItem::Metadata(
-                        ImportOrExportMetadataItem {
-                            lot,
-                            source,
-                            identifier,
-                            collections: vec![DefaultCollection::Owned.to_string()],
-                            ..Default::default()
-                        },
-                    ));
-                }
-            }
+            result
+                .completed
+                .push(ImportCompletedItem::Metadata(ImportOrExportMetadataItem {
+                    lot,
+                    source,
+                    identifier,
+                    collections: vec![DefaultCollection::Owned.to_string()],
+                    ..Default::default()
+                }));
         }
-        Ok(result)
     }
+    todo!()
 }
diff --git a/crates/services/integration/src/yank/komga.rs b/crates/services/integration/src/yank/komga.rs
index 43442266d3..fbd9c53c07 100644
--- a/crates/services/integration/src/yank/komga.rs
+++ b/crates/services/integration/src/yank/komga.rs
@@ -180,337 +180,342 @@ impl KomgaEventHandler {
     }
 }
 
-pub(crate) struct KomgaYankIntegration {
-    base_url: String,
-    username: String,
-    password: String,
-    source: MediaSource,
-    db: DatabaseConnection,
-    sync_to_owned_collection: Option<bool>,
-}
-
 type ProcessEventReturn = (CommitMediaInput, ImportOrExportMetadataItemSeen);
 
-impl KomgaYankIntegration {
-    pub fn new(
-        base_url: String,
-        username: String,
-        password: String,
-        source: MediaSource,
-        db: DatabaseConnection,
-        sync_to_owned_collection: Option<bool>,
-    ) -> Self {
-        Self {
-            db,
-            source,
-            base_url,
-            username,
-            password,
-            sync_to_owned_collection,
-        }
-    }
-
-    /// Generates the sse listener for komga. This is intended to be run from another
-    /// thread if you run this in the main thread it will lock it up
-    ///
-    /// # Arguments
-    ///
-    /// * `sender`: The unbounded sender, lifetime of this sender is the lifetime of this
-    ///             function so the sender doesn't need global lifetime
-    /// * `base_url`: URL for komga
-    /// * `komga_username`: The komga username
-    /// * `komga_password`: The komga password
-    ///
-    /// returns: Never Returns
-    async fn sse_listener(
-        sender: mpsc::UnboundedSender<komga_events::Data>,
-        base_url: String,
-        komga_username: String,
-        komga_password: String,
-    ) -> Result<(), Box<dyn std::error::Error>> {
-        let url = format!("{}/sse/v1", base_url);
-        let client = get_base_http_client(None);
-
-        loop {
-            let response = client
-                .get(format!("{}/events", url))
-                .basic_auth(komga_username.to_string(), Some(komga_password.to_string()))
-                .send()
-                .await
-                .context("Failed to send request")?;
-
-            let mut stream = response.bytes_stream().eventsource();
-
-            while let Some(event) = stream.next().await {
-                let event = event.context("Failed to get next event")?;
-                ryot_log!(debug, ?event, "Received SSE event");
-
-                // We could also handle ReadProgressDeleted here but I don't
-                // think we want to handle any deletions like this
-                if event.event == "ReadProgressChanged" {
-                    match serde_json::from_str::<komga_events::Data>(&event.data) {
-                        Ok(read_progress) => {
-                            if sender.send(read_progress).is_err() {
-                                ryot_log!(debug, "Receiver dropped, exiting SSE listener");
-                                break;
-                            }
+/// Generates the sse listener for komga. This is intended to be run from another
+/// thread if you run this in the main thread it will lock it up
+///
+/// # Arguments
+///
+/// * `sender`: The unbounded sender, lifetime of this sender is the lifetime of this
+///             function so the sender doesn't need global lifetime
+/// * `base_url`: URL for komga
+/// * `komga_username`: The komga username
+/// * `komga_password`: The komga password
+///
+/// returns: Never Returns
+async fn sse_listener(
+    sender: mpsc::UnboundedSender<komga_events::Data>,
+    base_url: String,
+    komga_username: String,
+    komga_password: String,
+) -> Result<(), Box<dyn std::error::Error>> {
+    let url = format!("{}/sse/v1", base_url);
+    let client = get_base_http_client(None);
+
+    loop {
+        let response = client
+            .get(format!("{}/events", url))
+            .basic_auth(komga_username.to_string(), Some(komga_password.to_string()))
+            .send()
+            .await
+            .context("Failed to send request")?;
+
+        let mut stream = response.bytes_stream().eventsource();
+
+        while let Some(event) = stream.next().await {
+            let event = event.context("Failed to get next event")?;
+            ryot_log!(debug, ?event, "Received SSE event");
+
+            // We could also handle ReadProgressDeleted here but I don't
+            // think we want to handle any deletions like this
+            if event.event == "ReadProgressChanged" {
+                match serde_json::from_str::<komga_events::Data>(&event.data) {
+                    Ok(read_progress) => {
+                        if sender.send(read_progress).is_err() {
+                            ryot_log!(debug, "Receiver dropped, exiting SSE listener");
+                            break;
                         }
-                        Err(e) => {
-                            ryot_log!(warn, error = ?e, data = ?event.data,
+                    }
+                    Err(e) => {
+                        ryot_log!(warn, error = ?e, data = ?event.data,
                                 "Failed to parse ReadProgressChanged event data");
-                        }
                     }
-                } else {
-                    ryot_log!(trace, event_type = ?event.event, "Received unhandled event type");
                 }
+            } else {
+                ryot_log!(trace, event_type = ?event.event, "Received unhandled event type");
             }
-
-            ryot_log!(trace, "SSE listener finished");
-            tokio::time::sleep(tokio::time::Duration::from_secs(30)).await;
-        }
-    }
-
-    /// Fetches an API request to the provided client URL like
-    /// `https://acme.com/api_endpoint/api_id`
-    ///
-    /// # Arguments
-    ///
-    /// * `client`: Pre-populated client. Use `get_base_http_client` to construct this
-    /// * `cookie`: The komga cookie with the remember-me included
-    /// * `api_endpoint`: Endpoint which comes after the base_url doesn't require a
-    ///   prepended `/`
-    /// * `api_id`: The ID of the object you are searching for added to the end of the
-    ///             api_endpoint doesn't require a prepended `/`
-    ///
-    /// returns: This only preforms basic error handling on the json parsing
-    async fn fetch_api<T: DeserializeOwned>(
-        &self,
-        client: &reqwest::Client,
-        api_endpoint: &str,
-        api_id: &str,
-    ) -> Result<T> {
-        client
-            .get(format!("{}/{}", api_endpoint, api_id))
-            .basic_auth(self.username.clone(), Some(self.password.clone()))
-            .send()
-            .await?
-            .error_for_status()?
-            .json::<T>()
-            .await
-            .map_err(|e| anyhow!("Failed to parse JSON: {}", e))
-    }
-
-    /// Finds the metadata provider and ID of the provided series
-    ///
-    /// # Arguments
-    ///
-    /// * `series`: The series object from which we want to grab the provider from. There
-    ///             should be a links section which is populated with urls from which we
-    ///             can extract the series ID. If not a simple search of the db for a manga
-    ///             with the same title will be preformed
-    ///
-    /// returns: This contains the MediaSource and the ID of the series.
-    async fn find_provider_and_id(
-        &self,
-        series: &komga_series::Item,
-    ) -> Result<(MediaSource, Option<String>)> {
-        let providers = series.metadata.find_providers();
-        if !providers.is_empty() {
-            Ok(providers
-                .iter()
-                .find(|x| x.0 == self.source)
-                .cloned()
-                .or_else(|| providers.first().cloned())
-                .unwrap_or_default())
-        } else {
-            let db_manga = Metadata::find()
-                .filter(metadata::Column::Lot.eq(MediaLot::Manga))
-                .filter(Expr::col(metadata::Column::Title).eq(&series.name))
-                .one(&self.db)
-                .await?;
-
-            Ok(db_manga
-                .map(|manga| (manga.source, Some(manga.identifier)))
-                .unwrap_or_default())
         }
-    }
 
-    fn calculate_percentage(&self, current_page: i32, total_page: i32) -> Decimal {
-        if total_page == 0 {
-            return dec!(0);
-        }
-        let percentage = (current_page as f64 / total_page as f64) * 100.0;
-        Decimal::from_f64(percentage).unwrap_or(dec!(0))
+        ryot_log!(trace, "SSE listener finished");
+        tokio::time::sleep(tokio::time::Duration::from_secs(30)).await;
     }
+}
 
-    async fn process_events(&self, data: komga_events::Data) -> Result<ProcessEventReturn> {
-        let url = format!("{}/api/v1", self.base_url);
-        let client = get_base_http_client(None);
+/// Fetches an API request to the provided client URL like
+/// `https://acme.com/api_endpoint/api_id`
+///
+/// # Arguments
+///
+/// * `client`: Pre-populated client. Use `get_base_http_client` to construct this
+/// * `cookie`: The komga cookie with the remember-me included
+/// * `api_endpoint`: Endpoint which comes after the base_url doesn't require a
+///   prepended `/`
+/// * `api_id`: The ID of the object you are searching for added to the end of the
+///             api_endpoint doesn't require a prepended `/`
+///
+/// returns: This only preforms basic error handling on the json parsing
+async fn fetch_api<T: DeserializeOwned>(
+    username: &String,
+    password: &String,
+    client: &reqwest::Client,
+    api_endpoint: &str,
+    api_id: &str,
+) -> Result<T> {
+    client
+        .get(format!("{}/{}", api_endpoint, api_id))
+        .basic_auth(username, Some(password))
+        .send()
+        .await?
+        .error_for_status()?
+        .json::<T>()
+        .await
+        .map_err(|e| anyhow!("Failed to parse JSON: {}", e))
+}
 
-        let book: komga_book::Item = self
-            .fetch_api(&client, &format!("{}/books", &url), &data.book_id)
-            .await?;
-        let series: komga_series::Item = self
-            .fetch_api(&client, &format!("{}/series", &url), &book.series_id)
+/// Finds the metadata provider and ID of the provided series
+///
+/// # Arguments
+///
+/// * `series`: The series object from which we want to grab the provider from. There
+///             should be a links section which is populated with urls from which we
+///             can extract the series ID. If not a simple search of the db for a manga
+///             with the same title will be preformed
+///
+/// returns: This contains the MediaSource and the ID of the series.
+async fn find_provider_and_id(
+    source: MediaSource,
+    db: &DatabaseConnection,
+    series: &komga_series::Item,
+) -> Result<(MediaSource, Option<String>)> {
+    let providers = series.metadata.find_providers();
+    if !providers.is_empty() {
+        Ok(providers
+            .iter()
+            .find(|x| x.0 == source)
+            .cloned()
+            .or_else(|| providers.first().cloned())
+            .unwrap_or_default())
+    } else {
+        let db_manga = Metadata::find()
+            .filter(metadata::Column::Lot.eq(MediaLot::Manga))
+            .filter(Expr::col(metadata::Column::Title).eq(&series.name))
+            .one(db)
             .await?;
 
-        let (source, id) = self.find_provider_and_id(&series).await?;
-
-        let Some(id) = id else {
-            let msg = format!(
-                "No MAL URL or database entry found for manga: {}",
-                series.name
-            );
-            ryot_log!(debug, msg);
-            bail!(msg)
-        };
-
-        Ok((
-            CommitMediaInput {
-                source,
-                identifier: id,
-                lot: MediaLot::Manga,
-                force_update: None,
-            },
-            ImportOrExportMetadataItemSeen {
-                progress: Some(
-                    self.calculate_percentage(book.read_progress.page, book.media.pages_count),
-                ),
-                provider_watched_on: Some("Komga".to_string()),
-                manga_chapter_number: Some(book.metadata.number.parse().unwrap_or_default()),
-                ..Default::default()
-            },
-        ))
+        Ok(db_manga
+            .map(|manga| (manga.source, Some(manga.identifier)))
+            .unwrap_or_default())
     }
+}
 
-    async fn sync_manga_collection(&self, result: &mut ImportResult) -> Result<()> {
-        let url = &format!("{}/api/v1", self.base_url);
-        let client = get_base_http_client(None);
+fn calculate_percentage(current_page: i32, total_page: i32) -> Decimal {
+    if total_page == 0 {
+        return dec!(0);
+    }
+    let percentage = (current_page as f64 / total_page as f64) * 100.0;
+    Decimal::from_f64(percentage).unwrap_or(dec!(0))
+}
 
-        let series: komga_series::Response = client
-            .get(format!("{}/{}", url, "series?unpaged=true"))
-            .basic_auth(&self.username, Some(&self.password))
-            .send()
-            .await?
-            .error_for_status()?
-            .json()
-            .await?;
+async fn process_events(
+    username: &String,
+    password: &String,
+    base_url: &String,
+    source: MediaSource,
+    db: &DatabaseConnection,
+    data: komga_events::Data,
+) -> Result<ProcessEventReturn> {
+    let url = format!("{}/api/v1", base_url);
+    let client = get_base_http_client(None);
+
+    let book: komga_book::Item = fetch_api(
+        username,
+        password,
+        &client,
+        &format!("{}/books", &url),
+        &data.book_id,
+    )
+    .await?;
+    let series: komga_series::Item = fetch_api(
+        username,
+        password,
+        &client,
+        &format!("{}/series", &url),
+        &book.series_id,
+    )
+    .await?;
+
+    let (source, id) = find_provider_and_id(source, db, &series).await?;
+
+    let Some(id) = id else {
+        let msg = format!(
+            "No MAL URL or database entry found for manga: {}",
+            series.name
+        );
+        ryot_log!(debug, msg);
+        bail!(msg)
+    };
+
+    Ok((
+        CommitMediaInput {
+            source,
+            identifier: id,
+            lot: MediaLot::Manga,
+            force_update: None,
+        },
+        ImportOrExportMetadataItemSeen {
+            progress: Some(calculate_percentage(
+                book.read_progress.page,
+                book.media.pages_count,
+            )),
+            provider_watched_on: Some("Komga".to_string()),
+            manga_chapter_number: Some(book.metadata.number.parse().unwrap_or_default()),
+            ..Default::default()
+        },
+    ))
+}
 
-        // Hashmap for if you have the same manga in multiple languages it will appear
-        // multiple times this prevents us from double committing an identifier
-        let unique_collection_updates: HashMap<String, _> = stream::iter(series.content)
-            .filter_map(|book| async move {
-                match self.find_provider_and_id(&book).await {
-                    Ok((source, Some(id))) => Some((
-                        id.clone(),
-                        ImportCompletedItem::Metadata(ImportOrExportMetadataItem {
-                            identifier: id,
-                            lot: MediaLot::Manga,
-                            source,
-                            collections: vec![DefaultCollection::Owned.to_string()],
-                            ..Default::default()
-                        }),
-                    )),
-                    _ => {
-                        tracing::debug!("No URL or database entry found for manga: {}", book.name);
-                        None
-                    }
+pub async fn yank_progress(
+    base_url: String,
+    username: String,
+    password: String,
+    source: MediaSource,
+    db: &DatabaseConnection,
+) -> Result<ImportResult> {
+    let mut result = ImportResult::default();
+    // DEV: This object needs global lifetime so we can continue to use the receiver if
+    // we ever create more SSE Objects we may want to implement a higher level
+    // Controller or make a housekeeping function to make sure the background threads
+    // are running correctly and kill them when the app is killed.
+    static SSE_LISTS: KomgaEventHandler = KomgaEventHandler::new();
+
+    let mutex_receiver = SSE_LISTS.get_receiver();
+    let mut receiver = {
+        let mut guard = mutex_receiver.lock().unwrap();
+        guard.take()
+    };
+
+    if receiver.is_none() {
+        let (tx, rx) = mpsc::unbounded_channel::<komga_events::Data>();
+        receiver = Some(rx);
+
+        let base_url = base_url.to_string();
+        let mutex_task = SSE_LISTS.get_task();
+        let komga_username = username.to_string();
+        let komga_password = password.to_string();
+
+        mutex_task.get_or_init(|| {
+            tokio::spawn(async move {
+                if let Err(e) = sse_listener(tx, base_url, komga_username, komga_password).await {
+                    ryot_log!(error, "SSE listener error: {}", e);
                 }
-            })
-            .collect()
-            .await;
-        result
-            .completed
-            .extend(unique_collection_updates.into_values());
-        Ok(())
+            });
+        });
     }
 
-    pub async fn yank_progress(&self) -> Result<ImportResult> {
-        let mut result = ImportResult::default();
-        // DEV: This object needs global lifetime so we can continue to use the receiver if
-        // we ever create more SSE Objects we may want to implement a higher level
-        // Controller or make a housekeeping function to make sure the background threads
-        // are running correctly and kill them when the app is killed.
-        static SSE_LISTS: KomgaEventHandler = KomgaEventHandler::new();
-
-        let mutex_receiver = SSE_LISTS.get_receiver();
-        let mut receiver = {
-            let mut guard = mutex_receiver.lock().unwrap();
-            guard.take()
-        };
-
-        if receiver.is_none() {
-            let (tx, rx) = mpsc::unbounded_channel::<komga_events::Data>();
-            receiver = Some(rx);
-
-            let base_url = self.base_url.to_string();
-            let mutex_task = SSE_LISTS.get_task();
-            let komga_username = self.username.to_string();
-            let komga_password = self.password.to_string();
-
-            mutex_task.get_or_init(|| {
-                tokio::spawn(async move {
-                    if let Err(e) =
-                        Self::sse_listener(tx, base_url, komga_username, komga_password).await
-                    {
-                        ryot_log!(error, "SSE listener error: {}", e);
-                    }
-                });
-            });
-        }
+    // Use hashmap here so we don't dupe pulls for a single book
+    let mut unique_media_items: HashMap<String, ProcessEventReturn> = HashMap::new();
 
-        // Use hashmap here so we don't dupe pulls for a single book
-        let mut unique_media_items: HashMap<String, ProcessEventReturn> = HashMap::new();
-
-        if let Some(mut recv) = receiver {
-            loop {
-                match recv.try_recv() {
-                    Ok(event) => {
-                        ryot_log!(debug, "Received event {:?}", event);
-                        match unique_media_items.entry(event.book_id.clone()) {
-                            Entry::Vacant(entry) => {
-                                if let Ok(processed_event) =
-                                    self.process_events(event.clone()).await
-                                {
-                                    entry.insert(processed_event);
-                                } else {
-                                    ryot_log!(
-                                        warn,
-                                        "Failed to process event for book_id: {}",
-                                        event.book_id
-                                    );
-                                }
+    if let Some(mut recv) = receiver {
+        loop {
+            match recv.try_recv() {
+                Ok(event) => {
+                    ryot_log!(debug, "Received event {:?}", event);
+                    match unique_media_items.entry(event.book_id.clone()) {
+                        Entry::Vacant(entry) => {
+                            if let Ok(processed_event) = process_events(
+                                &username,
+                                &password,
+                                &base_url,
+                                source,
+                                db,
+                                event.clone(),
+                            )
+                            .await
+                            {
+                                entry.insert(processed_event);
+                            } else {
+                                ryot_log!(
+                                    warn,
+                                    "Failed to process event for book_id: {}",
+                                    event.book_id
+                                );
                             }
-                            _ => continue,
                         }
+                        _ => continue,
                     }
-                    Err(TryRecvError::Empty) => break,
-                    Err(e) => bail!("Receiver error: {}", e),
                 }
+                Err(TryRecvError::Empty) => break,
+                Err(e) => bail!("Receiver error: {}", e),
             }
-
-            // Put the receiver back
-            mutex_receiver.lock().unwrap().replace(recv);
         }
 
-        let media_items = unique_media_items.into_values().collect_vec();
-        ryot_log!(debug, "Media Items: {:?}", media_items);
-        media_items.into_iter().for_each(|(commit, hist)| {
-            result
-                .completed
-                .push(ImportCompletedItem::Metadata(ImportOrExportMetadataItem {
-                    lot: commit.lot,
-                    source: commit.source,
-                    seen_history: vec![hist],
-                    identifier: commit.identifier,
-                    ..Default::default()
-                }));
-        });
+        // Put the receiver back
+        mutex_receiver.lock().unwrap().replace(recv);
+    }
 
-        if let Some(true) = self.sync_to_owned_collection {
-            self.sync_manga_collection(&mut result).await?;
-        }
+    let media_items = unique_media_items.into_values().collect_vec();
+    ryot_log!(debug, "Media Items: {:?}", media_items);
+    media_items.into_iter().for_each(|(commit, hist)| {
+        result
+            .completed
+            .push(ImportCompletedItem::Metadata(ImportOrExportMetadataItem {
+                lot: commit.lot,
+                source: commit.source,
+                seen_history: vec![hist],
+                identifier: commit.identifier,
+                ..Default::default()
+            }));
+    });
 
-        Ok(result)
-    }
+    Ok(result)
+}
+
+pub async fn sync_to_owned_collection(
+    base_url: String,
+    username: String,
+    password: String,
+    source: MediaSource,
+    db: &DatabaseConnection,
+) -> Result<ImportResult> {
+    let mut result = ImportResult::default();
+    let url = &format!("{}/api/v1", base_url);
+    let client = get_base_http_client(None);
+
+    let series: komga_series::Response = client
+        .get(format!("{}/{}", url, "series?unpaged=true"))
+        .basic_auth(username, Some(password))
+        .send()
+        .await?
+        .error_for_status()?
+        .json()
+        .await?;
+
+    // Hashmap for if you have the same manga in multiple languages it will appear
+    // multiple times this prevents us from double committing an identifier
+    let unique_collection_updates: HashMap<String, _> = stream::iter(series.content)
+        .filter_map(|book| async move {
+            match find_provider_and_id(source, db, &book).await {
+                Ok((source, Some(id))) => Some((
+                    id.clone(),
+                    ImportCompletedItem::Metadata(ImportOrExportMetadataItem {
+                        identifier: id,
+                        lot: MediaLot::Manga,
+                        source,
+                        collections: vec![DefaultCollection::Owned.to_string()],
+                        ..Default::default()
+                    }),
+                )),
+                _ => {
+                    tracing::debug!("No URL or database entry found for manga: {}", book.name);
+                    None
+                }
+            }
+        })
+        .collect()
+        .await;
+    result
+        .completed
+        .extend(unique_collection_updates.into_values());
+    Ok(result)
 }
diff --git a/crates/services/integration/src/yank/mod.rs b/crates/services/integration/src/yank/mod.rs
index e35753d982..4f0af3beb0 100644
--- a/crates/services/integration/src/yank/mod.rs
+++ b/crates/services/integration/src/yank/mod.rs
@@ -1,2 +1,3 @@
 pub mod audiobookshelf;
 pub mod komga;
+pub mod plex;
diff --git a/crates/services/integration/src/yank/plex.rs b/crates/services/integration/src/yank/plex.rs
new file mode 100644
index 0000000000..83212a00a2
--- /dev/null
+++ b/crates/services/integration/src/yank/plex.rs
@@ -0,0 +1,70 @@
+use anyhow::Result;
+use application_utils::get_base_http_client;
+use common_models::DefaultCollection;
+use common_utils::ryot_log;
+use dependent_models::{ImportCompletedItem, ImportResult};
+use enums::{MediaLot, MediaSource};
+use media_models::ImportOrExportMetadataItem;
+use reqwest::header::{HeaderName, HeaderValue, ACCEPT};
+use specific_models::plex as plex_models;
+
+pub async fn sync_to_owned_collection(base_url: String, token: String) -> Result<ImportResult> {
+    let client = get_base_http_client(Some(vec![
+        (
+            HeaderName::from_static("x-plex-token"),
+            HeaderValue::from_str(&token).unwrap(),
+        ),
+        (ACCEPT, HeaderValue::from_static("application/json")),
+    ]));
+    let libraries = client
+        .get(format!("{}/library/sections", base_url))
+        .send()
+        .await?
+        .json::<plex_models::PlexMediaResponse<plex_models::PlexLibrary>>()
+        .await?;
+
+    let mut success_items = vec![];
+    for dir in libraries.media_container.directory {
+        ryot_log!(debug, "Processing directory {:?}", dir.title);
+        let item_type = dir.item_type.as_str();
+        if !["movie", "show"].contains(&item_type) {
+            ryot_log!(debug, "Skipping directory {:?}", dir.title);
+            continue;
+        }
+        let lot = match item_type {
+            "movie" => MediaLot::Movie,
+            "show" => MediaLot::Show,
+            _ => unreachable!(),
+        };
+        let items = client
+            .get(format!("{}/library/sections/{}/all", base_url, dir.key))
+            .query(&serde_json::json!({ "includeGuids": "1" }))
+            .send()
+            .await?
+            .json::<plex_models::PlexMediaResponse<plex_models::PlexMetadata>>()
+            .await?;
+        for (idx, item) in items.media_container.metadata.into_iter().enumerate() {
+            ryot_log!(debug, "Processing item {}", idx + 1);
+            let gu_ids = item.guid.unwrap_or_default();
+            let Some(tmdb_id) = gu_ids
+                .iter()
+                .find(|g| g.id.starts_with("tmdb://"))
+                .map(|g| &g.id[7..])
+            else {
+                continue;
+            };
+            success_items.push(ImportCompletedItem::Metadata(ImportOrExportMetadataItem {
+                lot,
+                source_id: item.key,
+                source: MediaSource::Tmdb,
+                identifier: tmdb_id.to_string(),
+                collections: vec![DefaultCollection::Owned.to_string()],
+                ..Default::default()
+            }));
+        }
+    }
+    Ok(ImportResult {
+        completed: success_items,
+        ..Default::default()
+    })
+}
diff --git a/crates/services/miscellaneous/Cargo.toml b/crates/services/miscellaneous/Cargo.toml
index e4dd2314c5..e7671178ed 100644
--- a/crates/services/miscellaneous/Cargo.toml
+++ b/crates/services/miscellaneous/Cargo.toml
@@ -30,11 +30,13 @@ sea-orm = { workspace = true }
 sea-query = { workspace = true }
 serde = { workspace = true }
 serde_json = { workspace = true }
+serde_with = { workspace = true }
 slug = { workspace = true }
 supporting-service = { path = "../supporting" }
 tokio = { workspace = true }
 tracing = { workspace = true }
 traits = { path = "../../traits" }
+unkey = { workspace = true }
 user-models = { path = "../../models/user" }
 uuid = { workspace = true }
 
diff --git a/crates/services/miscellaneous/src/lib.rs b/crates/services/miscellaneous/src/lib.rs
index 036d9e9646..772070bbce 100644
--- a/crates/services/miscellaneous/src/lib.rs
+++ b/crates/services/miscellaneous/src/lib.rs
@@ -10,15 +10,16 @@ use application_utils::{
 };
 use async_graphql::{Error, Result};
 use background::{ApplicationJob, CoreApplicationJob};
-use chrono::{Days, Duration, NaiveDate, Utc};
+use chrono::{Days, Duration, NaiveDate, TimeZone, Utc};
 use common_models::{
-    ApplicationCacheKey, BackendError, BackgroundJob, ChangeCollectionToEntityInput,
-    DefaultCollection, IdAndNamedObject, MediaStateChanged, SearchDetails, SearchInput, StoredUrl,
-    StringIdObject,
+    ApplicationCacheKey, ApplicationCacheValue, BackendError, BackgroundJob,
+    ChangeCollectionToEntityInput, DefaultCollection, IdAndNamedObject, MediaStateChanged,
+    SearchDetails, SearchInput, StoredUrl, StringIdObject,
 };
 use common_utils::{
-    get_first_and_last_day_of_month, ryot_log, IsFeatureEnabled, EXERCISE_LOT_MAPPINGS,
-    MEDIA_LOT_MAPPINGS, PAGE_SIZE, SHOW_SPECIAL_SEASON_NAMES,
+    convert_naive_to_utc, get_first_and_last_day_of_month, ryot_log, IsFeatureEnabled,
+    COMPILATION_TIMESTAMP, EXERCISE_LOT_MAPPINGS, MEDIA_LOT_MAPPINGS, PAGE_SIZE,
+    SHOW_SPECIAL_SEASON_NAMES,
 };
 use database_models::{
     access_link, application_cache, calendar_event, collection, collection_to_entity,
@@ -35,9 +36,9 @@ use database_models::{
     queued_notification, review, seen, user, user_to_entity,
 };
 use database_utils::{
-    add_entity_to_collection, apply_collection_filter, calculate_user_activities_and_summary,
-    entity_in_collections, entity_in_collections_with_collection_to_entity_ids, ilike_sql,
-    item_reviews, remove_entity_from_collection, revoke_access_link, user_by_id,
+    apply_collection_filter, calculate_user_activities_and_summary, entity_in_collections,
+    entity_in_collections_with_collection_to_entity_ids, ilike_sql, item_reviews,
+    remove_entity_from_collection, revoke_access_link, user_by_id,
 };
 use dependent_models::{
     CoreDetails, ExerciseFilters, ExerciseParameters, ExerciseParametersLotMapping, GenreDetails,
@@ -46,9 +47,10 @@ use dependent_models::{
     UserPersonDetails,
 };
 use dependent_utils::{
-    commit_metadata, commit_metadata_group_internal, commit_metadata_internal, commit_person,
-    create_partial_metadata, deploy_after_handle_media_seen_tasks, deploy_background_job,
-    deploy_update_metadata_job, first_metadata_image_as_url, get_metadata_provider,
+    add_entity_to_collection, commit_metadata, commit_metadata_group_internal,
+    commit_metadata_internal, commit_person, create_partial_metadata,
+    deploy_after_handle_media_seen_tasks, deploy_background_job, deploy_update_metadata_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, handle_after_media_seen_tasks, is_metadata_finished_by_user,
     metadata_images_as_urls, post_review, progress_update,
@@ -60,7 +62,7 @@ use enums::{
     ExerciseMuscle, MediaLot, MediaSource, MetadataToMetadataRelation, SeenState,
     UserToMediaReason,
 };
-use env_utils::APP_VERSION;
+use env_utils::{APP_VERSION, UNKEY_API_ID};
 use futures::TryStreamExt;
 use itertools::Itertools;
 use markdown::{to_html_with_options as markdown_to_html_opts, CompileOptions, Options};
@@ -113,9 +115,11 @@ use sea_query::{
     PostgresQueryBuilder, Query, SelectStatement,
 };
 use serde::{Deserialize, Serialize};
+use serde_with::skip_serializing_none;
 use supporting_service::SupportingService;
 use tokio::time::{sleep, Duration as TokioDuration};
 use traits::{MediaProvider, MediaProviderLanguages, TraceOk};
+use unkey::{models::VerifyKeyRequest, Client};
 use user_models::{DashboardElementLot, UserReviewScale};
 use uuid::Uuid;
 
@@ -182,13 +186,14 @@ FROM (
     SELECT "user_id", "metadata_id" FROM "user_to_entity"
     WHERE "user_id" IN (SELECT "id" from "user") AND "metadata_id" IS NOT NULL
 ) "sub"
-JOIN "metadata" "m" ON "sub"."metadata_id" = "m"."id" AND "m"."source" NOT IN ($1, $2, $3)
+JOIN "metadata" "m" ON "sub"."metadata_id" = "m"."id" AND "m"."source" NOT IN ($1, $2, $3, $4)
 ORDER BY RANDOM() LIMIT 10;
         "#,
             [
-                MediaSource::GoogleBooks.into(),
-                MediaSource::Itunes.into(),
                 MediaSource::Vndb.into(),
+                MediaSource::Itunes.into(),
+                MediaSource::Custom.into(),
+                MediaSource::GoogleBooks.into(),
             ],
         ))
         .all(&self.0.db)
@@ -253,7 +258,6 @@ ORDER BY RANDOM() LIMIT 10;
         let download_required = Exercise::find().count(&self.0.db).await? == 0;
         Ok(CoreDetails {
             page_size: PAGE_SIZE,
-            is_pro: self.0.is_pro,
             version: APP_VERSION.to_owned(),
             file_storage_enabled: files_enabled,
             frontend: self.0.config.frontend.clone(),
@@ -267,6 +271,7 @@ ORDER BY RANDOM() LIMIT 10;
             local_auth_disabled: self.0.config.users.disable_local_auth,
             repository_link: "https://github.com/ignisda/ryot".to_owned(),
             token_valid_for_days: self.0.config.users.token_valid_for_days,
+            is_server_key_validated: self.0.is_server_key_validated().await?,
             metadata_lot_source_mappings: MEDIA_LOT_MAPPINGS
                 .iter()
                 .map(|(lot, sources)| MetadataLotSourceMappings {
@@ -735,7 +740,7 @@ ORDER BY RANDOM() LIMIT 10;
             .unwrap();
         let seen_by: usize = seen_by.try_into().unwrap();
         let user_to_meta =
-            get_user_to_entity_association(&self.0.db, &user_id, metadata_id, EntityLot::Metadata)
+            get_user_to_entity_association(&self.0.db, &user_id, &metadata_id, EntityLot::Metadata)
                 .await;
         let average_rating = if reviews.is_empty() {
             None
@@ -804,6 +809,9 @@ ORDER BY RANDOM() LIMIT 10;
             } else {
                 None
             };
+        let recently_consumed =
+            get_entity_recently_consumed(&user_id, &metadata_id, EntityLot::Metadata, &self.0)
+                .await?;
         Ok(UserMetadataDetails {
             reviews,
             history,
@@ -813,6 +821,7 @@ ORDER BY RANDOM() LIMIT 10;
             show_progress,
             average_rating,
             podcast_progress,
+            recently_consumed,
             seen_by_user_count,
             seen_by_all_count: seen_by,
             has_interacted: user_to_meta.is_some(),
@@ -828,9 +837,12 @@ ORDER BY RANDOM() LIMIT 10;
         let reviews = item_reviews(&user_id, &person_id, EntityLot::Person, true, &self.0).await?;
         let collections =
             entity_in_collections(&self.0.db, &user_id, &person_id, EntityLot::Person).await?;
+        let recently_consumed =
+            get_entity_recently_consumed(&user_id, &person_id, EntityLot::Person, &self.0).await?;
         Ok(UserPersonDetails {
             reviews,
             collections,
+            recently_consumed,
         })
     }
 
@@ -854,9 +866,17 @@ ORDER BY RANDOM() LIMIT 10;
             &self.0,
         )
         .await?;
+        let recently_consumed = get_entity_recently_consumed(
+            &user_id,
+            &metadata_group_id,
+            EntityLot::MetadataGroup,
+            &self.0,
+        )
+        .await?;
         Ok(UserMetadataGroupDetails {
             reviews,
             collections,
+            recently_consumed,
         })
     }
 
@@ -945,10 +965,10 @@ ORDER BY RANDOM() LIMIT 10;
                             .into(),
                     )
                     .order_by_asc(calendar_event::Column::Date)
-                    .apply_if(end_date, |q, v| {
+                    .apply_if(start_date, |q, v| {
                         q.filter(calendar_event::Column::Date.gte(v))
                     })
-                    .apply_if(start_date, |q, v| {
+                    .apply_if(end_date, |q, v| {
                         q.filter(calendar_event::Column::Date.lte(v))
                     })
                     .limit(media_limit)
@@ -1009,7 +1029,7 @@ ORDER BY RANDOM() LIMIT 10;
         user_id: String,
         input: UserCalendarEventInput,
     ) -> Result<Vec<GroupedCalendarEvent>> {
-        let (end_date, start_date) = get_first_and_last_day_of_month(input.year, input.month);
+        let (start_date, end_date) = get_first_and_last_day_of_month(input.year, input.month);
         let events = self
             .get_calendar_events(user_id, false, Some(start_date), Some(end_date), None, None)
             .await?;
@@ -1030,11 +1050,11 @@ ORDER BY RANDOM() LIMIT 10;
         user_id: String,
         input: UserUpcomingCalendarEventInput,
     ) -> Result<Vec<GraphqlCalendarEvent>> {
-        let from_date = Utc::now().date_naive();
-        let (media_limit, to_date) = match input {
+        let start_date = Utc::now().date_naive();
+        let (media_limit, end_date) = match input {
             UserUpcomingCalendarEventInput::NextMedia(l) => (Some(l), None),
             UserUpcomingCalendarEventInput::NextDays(d) => {
-                (None, from_date.checked_add_days(Days::new(d)))
+                (None, start_date.checked_add_days(Days::new(d)))
             }
         };
         let preferences = user_by_id(&user_id, &self.0).await?.preferences.general;
@@ -1046,8 +1066,8 @@ ORDER BY RANDOM() LIMIT 10;
             .get_calendar_events(
                 user_id,
                 true,
-                to_date,
-                Some(from_date),
+                Some(start_date),
+                end_date,
                 media_limit,
                 element.and_then(|e| e.deduplicate_media),
             )
@@ -1560,17 +1580,12 @@ ORDER BY RANDOM() LIMIT 10;
             }
         }
         if let Some(_association) =
-            get_user_to_entity_association(&txn, &user_id, merge_into.clone(), EntityLot::Metadata)
-                .await
+            get_user_to_entity_association(&txn, &user_id, &merge_into, EntityLot::Metadata).await
         {
-            let old_association = get_user_to_entity_association(
-                &txn,
-                &user_id,
-                merge_from.clone(),
-                EntityLot::Metadata,
-            )
-            .await
-            .unwrap();
+            let old_association =
+                get_user_to_entity_association(&txn, &user_id, &merge_from, EntityLot::Metadata)
+                    .await
+                    .unwrap();
             let mut cloned: user_to_entity::ActiveModel = old_association.clone().into();
             cloned.needs_to_be_updated = ActiveValue::Set(Some(true));
             cloned.update(&txn).await?;
@@ -1781,10 +1796,8 @@ ORDER BY RANDOM() LIMIT 10;
             MediaSource::Audible => {
                 Box::new(AudibleService::new(&self.0.config.audio_books.audible).await)
             }
-            MediaSource::Listennotes => {
-                Box::new(ListennotesService::new(&self.0.config.podcasts).await)
-            }
-            MediaSource::Igdb => Box::new(IgdbService::new(&self.0.config.video_games).await),
+            MediaSource::Listennotes => Box::new(ListennotesService::new(self.0.clone()).await),
+            MediaSource::Igdb => Box::new(IgdbService::new(self.0.clone()).await),
             MediaSource::MangaUpdates => Box::new(
                 MangaUpdatesService::new(&self.0.config.anime_and_manga.manga_updates).await,
             ),
@@ -1837,13 +1850,8 @@ ORDER BY RANDOM() LIMIT 10;
         match review {
             Some(r) => {
                 if r.user_id == user_id {
-                    associate_user_with_entity(
-                        &self.0.db,
-                        &user_id,
-                        r.entity_id.clone(),
-                        r.entity_lot,
-                    )
-                    .await?;
+                    associate_user_with_entity(&self.0.db, &user_id, &r.entity_id, r.entity_lot)
+                        .await?;
                     r.delete(&self.0.db).await?;
                     Ok(true)
                 } else {
@@ -1886,7 +1894,7 @@ ORDER BY RANDOM() LIMIT 10;
             user_id: user_id.to_owned(),
             metadata_id: si.metadata_id.clone(),
         };
-        self.0.cache_service.delete(cache).await?;
+        self.0.cache_service.expire_key(cache).await?;
         let seen_id = si.id.clone();
         let metadata_id = si.metadata_id.clone();
         if &si.user_id != user_id {
@@ -1895,7 +1903,7 @@ ORDER BY RANDOM() LIMIT 10;
             ));
         }
         si.delete(&self.0.db).await.trace_ok();
-        associate_user_with_entity(&self.0.db, user_id, metadata_id, EntityLot::Metadata).await?;
+        associate_user_with_entity(&self.0.db, user_id, &metadata_id, EntityLot::Metadata).await?;
         deploy_after_handle_media_seen_tasks(cloned_seen, &self.0).await?;
         Ok(StringIdObject { id: seen_id })
     }
@@ -3083,6 +3091,73 @@ ORDER BY RANDOM() LIMIT 10;
         Ok(())
     }
 
+    async fn get_is_server_key_validated(&self) -> bool {
+        let pro_key = &self.0.config.server.pro_key;
+        if pro_key.is_empty() {
+            return false;
+        }
+        ryot_log!(debug, "Verifying pro key for API ID: {:#?}", UNKEY_API_ID);
+        let compile_timestamp = Utc.timestamp_opt(COMPILATION_TIMESTAMP, 0).unwrap();
+        #[skip_serializing_none]
+        #[derive(Debug, Serialize, Clone, Deserialize)]
+        struct Meta {
+            expiry: Option<NaiveDate>,
+        }
+        let unkey_client = Client::new("public");
+        let verify_request = VerifyKeyRequest::new(pro_key, &UNKEY_API_ID.to_string());
+        let validated_key = match unkey_client.verify_key(verify_request).await {
+            Ok(verify_response) => {
+                if !verify_response.valid {
+                    ryot_log!(debug, "Pro key is no longer valid.");
+                    return false;
+                }
+                verify_response
+            }
+            Err(verify_error) => {
+                ryot_log!(debug, "Pro key verification error: {:?}", verify_error);
+                return false;
+            }
+        };
+        let key_meta = validated_key
+            .meta
+            .map(|meta| serde_json::from_value::<Meta>(meta).unwrap());
+        ryot_log!(debug, "Expiry: {:?}", key_meta.clone().map(|m| m.expiry));
+        if let Some(meta) = key_meta {
+            if let Some(expiry) = meta.expiry {
+                if compile_timestamp > convert_naive_to_utc(expiry) {
+                    ryot_log!(warn, "Pro key has expired. Please renew your subscription.");
+                    return false;
+                }
+            }
+        }
+        ryot_log!(debug, "Pro key verified successfully");
+        true
+    }
+
+    pub async fn perform_server_key_validation(&self) -> Result<()> {
+        let is_server_key_validated = self.get_is_server_key_validated().await;
+        let cs = &self.0.cache_service;
+        if is_server_key_validated {
+            cs.set_with_expiry(
+                ApplicationCacheKey::ServerKeyValidated,
+                None,
+                ApplicationCacheValue::Empty,
+            )
+            .await?;
+        } else {
+            cs.expire_key(ApplicationCacheKey::ServerKeyValidated)
+                .await?;
+        }
+        Ok(())
+    }
+
+    pub async fn sync_integrations_data_to_owned_collection(&self) -> Result<()> {
+        self.0
+            .perform_application_job(ApplicationJob::SyncIntegrationsData)
+            .await?;
+        Ok(())
+    }
+
     pub async fn perform_background_jobs(&self) -> Result<()> {
         ryot_log!(debug, "Starting background jobs...");
 
@@ -3116,6 +3191,10 @@ ORDER BY RANDOM() LIMIT 10;
             .trace_ok();
         ryot_log!(trace, "Removing old user summaries and regenerating them");
         self.regenerate_user_summaries().await.trace_ok();
+        ryot_log!(trace, "Syncing integrations data to owned collection");
+        self.sync_integrations_data_to_owned_collection()
+            .await
+            .trace_ok();
         ryot_log!(trace, "Removing useless data");
         self.remove_useless_data().await.trace_ok();
         ryot_log!(trace, "Putting entities in partial state");
diff --git a/crates/services/statistics/Cargo.toml b/crates/services/statistics/Cargo.toml
index e3ffc1f337..0cfcc27f21 100644
--- a/crates/services/statistics/Cargo.toml
+++ b/crates/services/statistics/Cargo.toml
@@ -5,13 +5,16 @@ edition = "2021"
 
 [dependencies]
 async-graphql = { workspace = true }
+common-models = { path = "../../models/common" }
 database-models = { path = "../../models/database" }
 database-utils = { path = "../../utils/database" }
-dependent-models = { path = "../../models/dependent" }
-media-models = { path = "../../models/media" }
+enums = { path = "../../enums" }
+hashbag = { workspace = true }
+itertools = { workspace = true }
 sea-orm = { workspace = true }
 supporting-service = { path = "../supporting" }
+tokio = { workspace = true }
 tracing = { workspace = true }
 
 [package.metadata.cargo-machete]
-ignored = ["tracing"]
+ignored = ["tokio", "tracing"]
diff --git a/crates/services/statistics/src/lib.rs b/crates/services/statistics/src/lib.rs
index d854035275..e32c23f900 100644
--- a/crates/services/statistics/src/lib.rs
+++ b/crates/services/statistics/src/lib.rs
@@ -1,26 +1,82 @@
-use std::{fmt::Write, sync::Arc};
+use std::{cmp::Reverse, fmt::Write, sync::Arc};
 
 use async_graphql::Result;
+use common_models::{
+    ApplicationCacheKey, ApplicationCacheValue, ApplicationDateRange, DailyUserActivitiesResponse,
+    DailyUserActivitiesResponseGroupedBy, DailyUserActivityHourRecord, DailyUserActivityItem,
+    FitnessAnalyticsEquipment, FitnessAnalyticsExercise, FitnessAnalyticsMuscle, UserAnalytics,
+    UserAnalyticsInput, UserFitnessAnalytics,
+};
 use database_models::{daily_user_activity, prelude::DailyUserActivity};
 use database_utils::calculate_user_activities_and_summary;
-use dependent_models::DailyUserActivitiesResponse;
-use media_models::{
-    DailyUserActivitiesInput, DailyUserActivitiesResponseGroupedBy, DailyUserActivityItem,
-};
+use enums::{ExerciseEquipment, ExerciseMuscle};
+use hashbag::HashBag;
+use itertools::Itertools;
 use sea_orm::{
-    prelude::Expr,
-    sea_query::{Alias, Func},
-    ColumnTrait, EntityTrait, Iden, QueryFilter, QueryOrder, QuerySelect, QueryTrait,
+    prelude::{Date, Expr},
+    sea_query::{Alias, Func, NullOrdering},
+    ColumnTrait, DerivePartialModel, EntityTrait, FromQueryResult, Iden, Order, QueryFilter,
+    QueryOrder, QuerySelect, QueryTrait,
 };
 use supporting_service::SupportingService;
 
 pub struct StatisticsService(pub Arc<SupportingService>);
 
 impl StatisticsService {
-    pub async fn daily_user_activities(
+    pub async fn calculate_user_activities_and_summary(
         &self,
         user_id: &String,
-        input: DailyUserActivitiesInput,
+        calculate_from_beginning: bool,
+    ) -> Result<()> {
+        calculate_user_activities_and_summary(&self.0.db, user_id, calculate_from_beginning).await
+    }
+
+    pub async fn user_analytics_parameters(
+        &self,
+        user_id: &String,
+    ) -> Result<ApplicationDateRange> {
+        let cache_key = ApplicationCacheKey::UserAnalyticsParameters {
+            user_id: user_id.to_owned(),
+        };
+        if let Some(ApplicationCacheValue::UserAnalyticsParameters(cached)) =
+            self.0.cache_service.get_key(cache_key.clone()).await?
+        {
+            return Ok(cached);
+        }
+        let get_date = |ordering: Order| {
+            DailyUserActivity::find()
+                .filter(daily_user_activity::Column::UserId.eq(user_id))
+                .select_only()
+                .column(daily_user_activity::Column::Date)
+                .order_by_with_nulls(
+                    daily_user_activity::Column::Date,
+                    ordering,
+                    NullOrdering::Last,
+                )
+                .into_tuple::<Date>()
+                .one(&self.0.db)
+        };
+        let start_date = get_date(Order::Asc).await?;
+        let end_date = get_date(Order::Desc).await?;
+        let response = ApplicationDateRange {
+            end_date,
+            start_date,
+        };
+        self.0
+            .cache_service
+            .set_with_expiry(
+                cache_key,
+                Some(8),
+                ApplicationCacheValue::UserAnalyticsParameters(response.clone()),
+            )
+            .await?;
+        Ok(response)
+    }
+
+    async fn daily_user_activities(
+        &self,
+        user_id: &String,
+        input: UserAnalyticsInput,
     ) -> Result<DailyUserActivitiesResponse> {
         // TODO: https://github.com/SeaQL/sea-query/pull/825 when merged
         struct DateTrunc;
@@ -31,10 +87,10 @@ impl StatisticsService {
         }
         let precondition = DailyUserActivity::find()
             .filter(daily_user_activity::Column::UserId.eq(user_id))
-            .apply_if(input.end_date, |query, v| {
+            .apply_if(input.date_range.end_date, |query, v| {
                 query.filter(daily_user_activity::Column::Date.lte(v))
             })
-            .apply_if(input.start_date, |query, v| {
+            .apply_if(input.date_range.start_date, |query, v| {
                 query.filter(daily_user_activity::Column::Date.gte(v))
             })
             .select_only();
@@ -66,13 +122,17 @@ impl StatisticsService {
             }
         };
         let day_alias = Expr::col(Alias::new("day"));
+        let date_type = Alias::new("DATE");
         let items = precondition
             .column_as(
                 Expr::expr(Func::cast_as(
                     Func::cust(DateTrunc)
                         .arg(Expr::val(grouped_by.to_string()))
-                        .arg(daily_user_activity::Column::Date.into_expr()),
-                    Alias::new("DATE"),
+                        .arg(Func::coalesce([
+                            Expr::col(daily_user_activity::Column::Date).into(),
+                            Func::cast_as(Expr::val("2001-01-01"), date_type.clone()).into(),
+                        ])),
+                    date_type,
                 )),
                 "day",
             )
@@ -94,7 +154,7 @@ impl StatisticsService {
             )
             .column_as(
                 daily_user_activity::Column::MeasurementCount.sum(),
-                "measurement_count",
+                "user_measurement_count",
             )
             .column_as(
                 daily_user_activity::Column::WorkoutCount.sum(),
@@ -204,24 +264,125 @@ impl StatisticsService {
         })
     }
 
-    pub async fn latest_user_summary(&self, user_id: &String) -> Result<DailyUserActivityItem> {
-        let ls = self
-            .daily_user_activities(
-                user_id,
-                DailyUserActivitiesInput {
-                    group_by: Some(DailyUserActivitiesResponseGroupedBy::Millennium),
-                    ..Default::default()
-                },
-            )
-            .await?;
-        Ok(ls.items.last().cloned().unwrap_or_default())
-    }
-
-    pub async fn calculate_user_activities_and_summary(
+    pub async fn user_analytics(
         &self,
         user_id: &String,
-        calculate_from_beginning: bool,
-    ) -> Result<()> {
-        calculate_user_activities_and_summary(&self.0.db, user_id, calculate_from_beginning).await
+        input: UserAnalyticsInput,
+    ) -> Result<UserAnalytics> {
+        let cache_key = ApplicationCacheKey::UserAnalytics {
+            input: input.clone(),
+            user_id: user_id.to_owned(),
+        };
+        if let Some(ApplicationCacheValue::UserAnalytics(cached)) =
+            self.0.cache_service.get_key(cache_key.clone()).await?
+        {
+            return Ok(cached);
+        }
+        #[derive(Debug, DerivePartialModel, FromQueryResult)]
+        #[sea_orm(entity = "DailyUserActivity")]
+        pub struct CustomFitnessAnalytics {
+            pub workout_reps: i32,
+            pub workout_count: i32,
+            pub workout_weight: i32,
+            pub workout_duration: i32,
+            pub workout_distance: i32,
+            pub workout_rest_time: i32,
+            pub measurement_count: i32,
+            pub workout_personal_bests: i32,
+            pub workout_exercises: Vec<String>,
+            pub workout_muscles: Vec<ExerciseMuscle>,
+            pub workout_equipments: Vec<ExerciseEquipment>,
+            pub hour_records: Vec<DailyUserActivityHourRecord>,
+        }
+        let items = DailyUserActivity::find()
+            .filter(daily_user_activity::Column::UserId.eq(user_id))
+            .apply_if(input.date_range.start_date, |query, v| {
+                query.filter(daily_user_activity::Column::Date.gte(v))
+            })
+            .apply_if(input.date_range.end_date, |query, v| {
+                query.filter(daily_user_activity::Column::Date.lte(v))
+            })
+            .into_partial_model::<CustomFitnessAnalytics>()
+            .all(&self.0.db)
+            .await?;
+        let mut hours: Vec<DailyUserActivityHourRecord> = vec![];
+        items.iter().for_each(|item| {
+            for hour in item.hour_records.clone() {
+                let index = hours.iter().position(|h| h.hour == hour.hour);
+                if let Some(index) = index {
+                    hours.get_mut(index).unwrap().entities.extend(hour.entities);
+                } else {
+                    hours.push(hour);
+                }
+            }
+        });
+        let workout_reps = items.iter().map(|i| i.workout_reps).sum();
+        let workout_count = items.iter().map(|i| i.workout_count).sum();
+        let workout_weight = items.iter().map(|i| i.workout_weight).sum();
+        let workout_distance = items.iter().map(|i| i.workout_distance).sum();
+        let workout_duration = items.iter().map(|i| i.workout_duration).sum();
+        let workout_rest_time = items.iter().map(|i| i.workout_rest_time).sum();
+        let measurement_count = items.iter().map(|i| i.measurement_count).sum();
+        let workout_personal_bests = items.iter().map(|i| i.workout_personal_bests).sum();
+        let workout_muscles = items
+            .iter()
+            .flat_map(|i| i.workout_muscles.clone())
+            .collect::<HashBag<ExerciseMuscle>>()
+            .into_iter()
+            .map(|(muscle, count)| FitnessAnalyticsMuscle {
+                muscle,
+                count: count.try_into().unwrap(),
+            })
+            .sorted_by_key(|f| Reverse(f.count))
+            .collect_vec();
+        let workout_exercises = items
+            .iter()
+            .flat_map(|i| i.workout_exercises.clone())
+            .collect::<HashBag<String>>()
+            .into_iter()
+            .map(|(exercise_id, count)| FitnessAnalyticsExercise {
+                exercise: exercise_id,
+                count: count.try_into().unwrap(),
+            })
+            .sorted_by_key(|f| Reverse(f.count))
+            .collect_vec();
+        let workout_equipments = items
+            .iter()
+            .flat_map(|i| i.workout_equipments.clone())
+            .collect::<HashBag<ExerciseEquipment>>()
+            .into_iter()
+            .map(|(equipment, count)| FitnessAnalyticsEquipment {
+                equipment,
+                count: count.try_into().unwrap(),
+            })
+            .sorted_by_key(|f| Reverse(f.count))
+            .collect_vec();
+        let activities = self.daily_user_activities(user_id, input).await?;
+        let response = UserAnalytics {
+            hours,
+            activities,
+            fitness: UserFitnessAnalytics {
+                workout_reps,
+                workout_count,
+                workout_weight,
+                workout_muscles,
+                workout_distance,
+                workout_duration,
+                workout_rest_time,
+                workout_exercises,
+                measurement_count,
+                workout_equipments,
+                workout_personal_bests,
+            },
+        };
+        self.0
+            .cache_service
+            .set_with_expiry(
+                cache_key,
+                Some(2),
+                ApplicationCacheValue::UserAnalytics(response.clone()),
+            )
+            .await?;
+        Ok(response)
     }
 }
diff --git a/crates/services/supporting/Cargo.toml b/crates/services/supporting/Cargo.toml
index ca5ba20547..8ed1112008 100644
--- a/crates/services/supporting/Cargo.toml
+++ b/crates/services/supporting/Cargo.toml
@@ -8,6 +8,7 @@ async-graphql = { workspace = true }
 apalis = { workspace = true }
 background = { path = "../../background" }
 cache-service = { path = "../../services/cache" }
+common-models = { path = "../../models/common" }
 chrono-tz = { workspace = true }
 config = { path = "../../config" }
 file-storage-service = { path = "../../services/file-storage" }
diff --git a/crates/services/supporting/src/lib.rs b/crates/services/supporting/src/lib.rs
index f4699086d8..9a2c47f72c 100644
--- a/crates/services/supporting/src/lib.rs
+++ b/crates/services/supporting/src/lib.rs
@@ -4,12 +4,12 @@ use apalis::prelude::{MemoryStorage, MessageQueue};
 use async_graphql::Result;
 use background::{ApplicationJob, CoreApplicationJob};
 use cache_service::CacheService;
+use common_models::ApplicationCacheKey;
 use file_storage_service::FileStorageService;
 use openidconnect::core::CoreClient;
 use sea_orm::DatabaseConnection;
 
 pub struct SupportingService {
-    pub is_pro: bool,
     pub db: DatabaseConnection,
     pub timezone: chrono_tz::Tz,
     pub cache_service: CacheService,
@@ -24,7 +24,6 @@ pub struct SupportingService {
 impl SupportingService {
     #[allow(clippy::too_many_arguments)]
     pub async fn new(
-        is_pro: bool,
         db: &DatabaseConnection,
         timezone: chrono_tz::Tz,
         cache_service: CacheService,
@@ -36,7 +35,6 @@ impl SupportingService {
     ) -> Self {
         Self {
             config,
-            is_pro,
             timezone,
             oidc_client,
             cache_service,
@@ -64,4 +62,11 @@ impl SupportingService {
             .unwrap();
         Ok(())
     }
+
+    pub async fn is_server_key_validated(&self) -> Result<bool> {
+        self.cache_service
+            .get_key(ApplicationCacheKey::ServerKeyValidated)
+            .await
+            .map(|v| v.is_some())
+    }
 }
diff --git a/crates/services/user/src/lib.rs b/crates/services/user/src/lib.rs
index 091f4dbc88..6ce81ff644 100644
--- a/crates/services/user/src/lib.rs
+++ b/crates/services/user/src/lib.rs
@@ -13,8 +13,8 @@ use database_models::{
 };
 use database_utils::{
     admin_account_guard, create_or_update_collection,
-    deploy_job_to_calculate_user_activities_and_summary, ilike_sql, pro_instance_guard,
-    revoke_access_link, user_by_id,
+    deploy_job_to_calculate_user_activities_and_summary, ilike_sql, revoke_access_link,
+    server_key_validation_guard, user_by_id,
 };
 use dependent_models::UserDetailsResult;
 use enum_meta::Meta;
@@ -28,8 +28,8 @@ use media_models::{
     LoginResponse, LoginResult, OidcTokenOutput, PasswordUserInput, ProcessAccessLinkError,
     ProcessAccessLinkErrorVariant, ProcessAccessLinkInput, ProcessAccessLinkResponse,
     ProcessAccessLinkResult, RegisterError, RegisterErrorVariant, RegisterResult,
-    RegisterUserInput, UpdateUserInput, UpdateUserIntegrationInput,
-    UpdateUserNotificationPlatformInput, UserDetailsError, UserDetailsErrorVariant,
+    RegisterUserInput, UpdateUserIntegrationInput, UpdateUserNotificationPlatformInput,
+    UserDetailsError, UserDetailsErrorVariant,
 };
 use nanoid::nanoid;
 use notification_service::send_notification;
@@ -45,8 +45,8 @@ use sea_orm::{
 };
 use supporting_service::SupportingService;
 use user_models::{
-    DashboardElementLot, GridPacking, NotificationPlatformSpecifics, UserPreferences,
-    UserReviewScale,
+    DashboardElementLot, GridPacking, NotificationPlatformSpecifics, UpdateUserInput,
+    UserPreferences, UserReviewScale,
 };
 
 fn empty_nonce_verifier(_nonce: Option<&Nonce>) -> Result<(), String> {
@@ -99,7 +99,7 @@ impl UserService {
         input: CreateAccessLinkInput,
         user_id: String,
     ) -> Result<StringIdObject> {
-        pro_instance_guard(self.0.is_pro).await?;
+        server_key_validation_guard(self.0.is_server_key_validated().await?).await?;
         let new_link = access_link::ActiveModel {
             user_id: ActiveValue::Set(user_id),
             name: ActiveValue::Set(input.name),
@@ -191,7 +191,7 @@ impl UserService {
     }
 
     pub async fn revoke_access_link(&self, access_link_id: String) -> Result<bool> {
-        pro_instance_guard(self.0.is_pro).await?;
+        server_key_validation_guard(self.0.is_server_key_validated().await?).await?;
         revoke_access_link(&self.0.db, access_link_id).await
     }
 
@@ -380,9 +380,6 @@ impl UserService {
         if let Some(p) = input.password {
             user_obj.password = ActiveValue::Set(Some(p));
         }
-        if let Some(i) = input.extra_information {
-            user_obj.extra_information = ActiveValue::Set(Some(i));
-        }
         if let Some(l) = input.lot {
             user_obj.lot = ActiveValue::Set(l);
         }
@@ -635,6 +632,14 @@ impl UserService {
                                     preferences.features_enabled.fitness.templates =
                                         value_bool.unwrap()
                                 }
+
+                                _ => return Err(err()),
+                            },
+                            "analytics" => match right {
+                                "enabled" => {
+                                    preferences.features_enabled.analytics.enabled =
+                                        value_bool.unwrap()
+                                }
                                 _ => return Err(err()),
                             },
                             "media" => {
@@ -784,6 +789,9 @@ impl UserService {
         if let Some(d) = input.is_disabled {
             db_integration.is_disabled = ActiveValue::Set(Some(d));
         }
+        if let Some(d) = input.sync_to_owned_collection {
+            db_integration.sync_to_owned_collection = ActiveValue::Set(Some(d));
+        }
         db_integration.update(&self.0.db).await?;
         Ok(true)
     }
@@ -799,9 +807,9 @@ impl UserService {
             ));
         }
         let lot = match input.provider {
-            IntegrationProvider::Audiobookshelf | IntegrationProvider::Komga => {
-                IntegrationLot::Yank
-            }
+            IntegrationProvider::Audiobookshelf
+            | IntegrationProvider::Komga
+            | IntegrationProvider::PlexYank => IntegrationLot::Yank,
             IntegrationProvider::Radarr
             | IntegrationProvider::Sonarr
             | IntegrationProvider::JellyfinPush => IntegrationLot::Push,
@@ -814,6 +822,7 @@ impl UserService {
             minimum_progress: ActiveValue::Set(input.minimum_progress),
             maximum_progress: ActiveValue::Set(input.maximum_progress),
             provider_specifics: ActiveValue::Set(input.provider_specifics),
+            sync_to_owned_collection: ActiveValue::Set(input.sync_to_owned_collection),
             ..Default::default()
         };
         let integration = to_insert.insert(&self.0.db).await?;
diff --git a/crates/utils/database/src/lib.rs b/crates/utils/database/src/lib.rs
index 6177846295..34be1e3f4e 100644
--- a/crates/utils/database/src/lib.rs
+++ b/crates/utils/database/src/lib.rs
@@ -16,8 +16,8 @@ use database_models::{
     functions::associate_user_with_entity,
     metadata,
     prelude::{
-        AccessLink, Collection, CollectionToEntity, DailyUserActivity, Metadata, Review, Seen,
-        User, UserMeasurement, UserToEntity, Workout, WorkoutTemplate,
+        AccessLink, Collection, CollectionToEntity, DailyUserActivity, Exercise, Metadata, Review,
+        Seen, User, UserMeasurement, UserToEntity, Workout, WorkoutTemplate,
     },
     review, seen, user, user_measurement, user_to_entity, workout,
 };
@@ -96,8 +96,8 @@ pub async fn admin_account_guard(user_id: &String, ss: &Arc<SupportingService>)
     Ok(())
 }
 
-pub async fn pro_instance_guard(is_pro: bool) -> Result<()> {
-    if !is_pro {
+pub async fn server_key_validation_guard(is_server_key_validated: bool) -> Result<()> {
+    if !is_server_key_validated {
         return Err(Error::new(
             "This feature is only available on the Pro version",
         ));
@@ -112,10 +112,10 @@ pub async fn user_measurements_list(
 ) -> Result<Vec<user_measurement::Model>> {
     let resp = UserMeasurement::find()
         .apply_if(input.start_time, |query, v| {
-            query.filter(user_measurement::Column::Timestamp.lte(v))
+            query.filter(user_measurement::Column::Timestamp.gte(v))
         })
         .apply_if(input.end_time, |query, v| {
-            query.filter(user_measurement::Column::Timestamp.gte(v))
+            query.filter(user_measurement::Column::Timestamp.lte(v))
         })
         .filter(user_measurement::Column::UserId.eq(user_id))
         .order_by_asc(user_measurement::Column::Timestamp)
@@ -126,7 +126,7 @@ pub async fn user_measurements_list(
 
 type CteColAlias = collection_to_entity::Column;
 
-fn get_cte_column_from_lot(entity_lot: EntityLot) -> collection_to_entity::Column {
+pub fn get_cte_column_from_lot(entity_lot: EntityLot) -> collection_to_entity::Column {
     match entity_lot {
         EntityLot::Metadata => CteColAlias::MetadataId,
         EntityLot::Person => CteColAlias::PersonId,
@@ -270,74 +270,6 @@ where
     })
 }
 
-pub async fn add_entity_to_collection(
-    user_id: &String,
-    input: ChangeCollectionToEntityInput,
-    ss: &Arc<SupportingService>,
-) -> Result<bool> {
-    let collection = Collection::find()
-        .left_join(UserToEntity)
-        .filter(user_to_entity::Column::UserId.eq(user_id))
-        .filter(collection::Column::Name.eq(input.collection_name))
-        .one(&ss.db)
-        .await
-        .unwrap()
-        .unwrap();
-    let mut updated: collection::ActiveModel = collection.into();
-    updated.last_updated_on = ActiveValue::Set(Utc::now());
-    let collection = updated.update(&ss.db).await.unwrap();
-    let column = get_cte_column_from_lot(input.entity_lot);
-    let resp = if let Some(etc) = CollectionToEntity::find()
-        .filter(CteColAlias::CollectionId.eq(collection.id.clone()))
-        .filter(column.eq(input.entity_id.clone()))
-        .one(&ss.db)
-        .await?
-    {
-        let mut to_update: collection_to_entity::ActiveModel = etc.into();
-        to_update.last_updated_on = ActiveValue::Set(Utc::now());
-        to_update.update(&ss.db).await?
-    } else {
-        let mut created_collection = collection_to_entity::ActiveModel {
-            collection_id: ActiveValue::Set(collection.id),
-            information: ActiveValue::Set(input.information),
-            ..Default::default()
-        };
-        let id = input.entity_id.clone();
-        match input.entity_lot {
-            EntityLot::Metadata => created_collection.metadata_id = ActiveValue::Set(Some(id)),
-            EntityLot::Person => created_collection.person_id = ActiveValue::Set(Some(id)),
-            EntityLot::MetadataGroup => {
-                created_collection.metadata_group_id = ActiveValue::Set(Some(id))
-            }
-            EntityLot::Exercise => created_collection.exercise_id = ActiveValue::Set(Some(id)),
-            EntityLot::Workout => created_collection.workout_id = ActiveValue::Set(Some(id)),
-            EntityLot::WorkoutTemplate => {
-                created_collection.workout_template_id = ActiveValue::Set(Some(id))
-            }
-            EntityLot::Collection | EntityLot::Review | EntityLot::UserMeasurement => {
-                unreachable!()
-            }
-        }
-        let created = created_collection.insert(&ss.db).await?;
-        ryot_log!(debug, "Created collection to entity: {:?}", created);
-        match input.entity_lot {
-            EntityLot::Workout
-            | EntityLot::WorkoutTemplate
-            | EntityLot::Review
-            | EntityLot::UserMeasurement => {}
-            _ => {
-                associate_user_with_entity(&ss.db, user_id, input.entity_id, input.entity_lot)
-                    .await
-                    .ok();
-            }
-        }
-        created
-    };
-    ss.perform_application_job(ApplicationJob::HandleEntityAddedToCollectionEvent(resp.id))
-        .await?;
-    Ok(true)
-}
-
 pub fn user_claims_from_token(token: &str, jwt_secret: &str) -> Result<Claims> {
     verify(token, jwt_secret).map_err(|e| Error::new(format!("Encountered error: {:?}", e)))
 }
@@ -394,7 +326,7 @@ pub async fn remove_entity_from_collection(
         .exec(db)
         .await?;
     if input.entity_lot != EntityLot::Workout && input.entity_lot != EntityLot::WorkoutTemplate {
-        associate_user_with_entity(db, user_id, input.entity_id, input.entity_lot).await?;
+        associate_user_with_entity(db, user_id, &input.entity_id, input.entity_lot).await?;
     }
     Ok(StringIdObject { id: collect.id })
 }
@@ -597,15 +529,15 @@ pub async fn calculate_user_activities_and_summary(
             .order_by_desc(daily_user_activity::Column::Date)
             .one(db)
             .await?
-            .map(|i| i.date)
+            .and_then(|i| i.date)
             .unwrap_or_default(),
     };
     let mut activities = HashMap::new();
 
     fn get_activity_count<'a>(
-        activities: &'a mut HashMap<Date, daily_user_activity::Model>,
+        activities: &'a mut HashMap<Option<Date>, daily_user_activity::Model>,
         user_id: &'a String,
-        date: Date,
+        date: Option<Date>,
         entity_id: String,
         entity_lot: EntityLot,
         metadata_lot: Option<MediaLot>,
@@ -620,7 +552,11 @@ pub async fn calculate_user_activities_and_summary(
                 ..Default::default()
             });
         existing.entity_ids.push(entity_id.clone());
-        let hour = timestamp.hour();
+        let hour = if timestamp.minute() < 30 {
+            timestamp.hour()
+        } else {
+            timestamp.hour() + 1
+        };
         let maybe_idx = existing.hour_records.iter().position(|hr| hr.hour == hour);
         if let Some(idx) = maybe_idx {
             existing.hour_records.get_mut(idx).unwrap().entities.push(
@@ -677,12 +613,10 @@ pub async fn calculate_user_activities_and_summary(
         .await?;
 
     while let Some(seen) = seen_stream.try_next().await? {
-        let default_date = Date::from_ymd_opt(2023, 4, 3).unwrap(); // DEV: The first commit of Ryot
-        let date = seen.finished_on.unwrap_or(default_date);
         let activity = get_activity_count(
             &mut activities,
             user_id,
-            date,
+            seen.finished_on,
             seen.seen_id,
             EntityLot::Metadata,
             Some(seen.metadata_lot),
@@ -741,6 +675,12 @@ pub async fn calculate_user_activities_and_summary(
         };
     }
 
+    let exercises = Exercise::find().all(db).await.unwrap();
+    let user_exercises = UserToEntity::find()
+        .filter(user_to_entity::Column::UserId.eq(user_id))
+        .filter(user_to_entity::Column::ExerciseId.is_not_null())
+        .all(db)
+        .await?;
     let mut workout_stream = Workout::find()
         .filter(workout::Column::UserId.eq(user_id))
         .filter(workout::Column::EndTime.gte(start_from))
@@ -751,7 +691,7 @@ pub async fn calculate_user_activities_and_summary(
         let activity = get_activity_count(
             &mut activities,
             user_id,
-            date,
+            Some(date),
             workout.id,
             EntityLot::Workout,
             None,
@@ -764,7 +704,24 @@ pub async fn calculate_user_activities_and_summary(
         activity.workout_weight += workout_total.weight.to_i32().unwrap_or_default();
         activity.workout_reps += workout_total.reps.to_i32().unwrap_or_default();
         activity.workout_distance += workout_total.distance.to_i32().unwrap_or_default();
-        activity.workout_rest_time += workout_total.rest_time as i32;
+        activity.workout_rest_time += (workout_total.rest_time as i32) / 60;
+        for exercise in workout.information.exercises {
+            let db_ex = exercises.iter().find(|e| e.id == exercise.id).unwrap();
+            if user_exercises
+                .iter()
+                .find(|e| e.exercise_id == Some(db_ex.id.clone()))
+                .unwrap()
+                .exercise_extra_information
+                .as_ref()
+                .map(|d| d.settings.exclude_from_analytics)
+                .unwrap_or_default()
+            {
+                continue;
+            }
+            activity.workout_exercises.push(db_ex.name.clone());
+            activity.workout_equipments.extend(db_ex.equipment);
+            activity.workout_muscles.extend(db_ex.muscles.clone());
+        }
     }
 
     let mut measurement_stream = UserMeasurement::find()
@@ -777,7 +734,7 @@ pub async fn calculate_user_activities_and_summary(
         let activity = get_activity_count(
             &mut activities,
             user_id,
-            date,
+            Some(date),
             measurement.timestamp.to_string(),
             EntityLot::UserMeasurement,
             None,
@@ -796,7 +753,7 @@ pub async fn calculate_user_activities_and_summary(
         let activity = get_activity_count(
             &mut activities,
             user_id,
-            date,
+            Some(date),
             review.id,
             EntityLot::Review,
             None,
@@ -819,8 +776,10 @@ pub async fn calculate_user_activities_and_summary(
             .one(db)
             .await?
         {
+            ryot_log!(debug, "Deleting activity = {:?}", activity.date);
             entity.delete(db).await?;
         }
+        ryot_log!(debug, "Inserting activity = {:?}", activity.date);
         let total_review_count = activity.metadata_review_count
             + activity.collection_review_count
             + activity.metadata_group_review_count
@@ -848,6 +807,7 @@ pub async fn calculate_user_activities_and_summary(
             + activity.video_game_duration;
         activity.hour_records.sort_by_key(|hr| hr.hour);
         let mut model: daily_user_activity::ActiveModel = activity.clone().into();
+        model.id = ActiveValue::NotSet;
         model.total_review_count = ActiveValue::Set(total_review_count);
         model.total_metadata_count = ActiveValue::Set(total_metadata_count);
         model.total_count = ActiveValue::Set(total_count);
@@ -871,11 +831,19 @@ pub async fn deploy_job_to_calculate_user_activities_and_summary(
     .unwrap();
 }
 
-pub async fn deploy_job_to_re_evaluate_user_workouts(
+pub async fn schedule_user_for_workout_revision(
     user_id: &String,
     ss: &Arc<SupportingService>,
-) {
-    ss.perform_application_job(ApplicationJob::ReEvaluateUserWorkouts(user_id.to_owned()))
-        .await
-        .unwrap();
+) -> Result<()> {
+    let user = User::find_by_id(user_id)
+        .one(&ss.db)
+        .await?
+        .ok_or_else(|| Error::new("User with the given ID does not exist"))?;
+    let mut extra_information = user.extra_information.clone().unwrap_or_default();
+    extra_information.scheduled_for_workout_revision = true;
+    let mut user: user::ActiveModel = user.into();
+    user.extra_information = ActiveValue::Set(Some(extra_information));
+    user.update(&ss.db).await?;
+    ryot_log!(debug, "Scheduled user for workout revision: {:?}", user_id);
+    Ok(())
 }
diff --git a/crates/utils/dependent/Cargo.toml b/crates/utils/dependent/Cargo.toml
index feee0e3d14..66681ab26a 100644
--- a/crates/utils/dependent/Cargo.toml
+++ b/crates/utils/dependent/Cargo.toml
@@ -4,7 +4,6 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-anyhow = { workspace = true }
 application-utils = { path = "../application" }
 async-graphql = { workspace = true }
 background = { path = "../../background" }
diff --git a/crates/utils/dependent/src/lib.rs b/crates/utils/dependent/src/lib.rs
index a7a8e3b08a..40ac7e1e5f 100644
--- a/crates/utils/dependent/src/lib.rs
+++ b/crates/utils/dependent/src/lib.rs
@@ -1,18 +1,19 @@
 use std::{cmp::Reverse, collections::HashMap, future::Future, iter::zip, sync::Arc};
 
-use anyhow::{bail, Result as AnyhowResult};
 use application_utils::get_current_date;
 use async_graphql::{Enum, Error, Result};
 use background::{ApplicationJob, CoreApplicationJob};
 use chrono::Utc;
 use common_models::{
-    ApplicationCacheKey, BackgroundJob, ChangeCollectionToEntityInput, DefaultCollection,
-    MediaStateChanged, StoredUrl, StringIdObject,
+    ApplicationCacheKey, ApplicationCacheValue, BackgroundJob, ChangeCollectionToEntityInput,
+    DefaultCollection, MediaStateChanged, StoredUrl, StringIdObject,
 };
 use common_utils::{ryot_log, EXERCISE_LOT_MAPPINGS, SHOW_SPECIAL_SEASON_NAMES};
 use database_models::{
-    collection_to_entity, exercise, genre, metadata, metadata_group, metadata_to_genre,
-    metadata_to_metadata, metadata_to_person, monitored_entity, person,
+    collection, collection_to_entity, exercise,
+    functions::associate_user_with_entity,
+    genre, metadata, metadata_group, metadata_to_genre, metadata_to_metadata, metadata_to_person,
+    monitored_entity, person,
     prelude::{
         Collection, CollectionToEntity, Exercise, Genre, Metadata, MetadataGroup, MetadataToGenre,
         MetadataToMetadata, MetadataToPerson, MonitoredEntity, Person, Seen, UserToEntity, Workout,
@@ -20,8 +21,8 @@ use database_models::{
     queued_notification, review, seen, user_measurement, user_to_entity, workout,
 };
 use database_utils::{
-    add_entity_to_collection, admin_account_guard, create_or_update_collection,
-    deploy_job_to_re_evaluate_user_workouts, remove_entity_from_collection, user_by_id,
+    admin_account_guard, create_or_update_collection, get_cte_column_from_lot,
+    remove_entity_from_collection, schedule_user_for_workout_revision, user_by_id,
 };
 use dependent_models::{ImportCompletedItem, ImportResult};
 use enums::{
@@ -120,12 +121,7 @@ pub async fn get_google_books_service(config: &config::AppConfig) -> Result<Goog
 pub async fn get_tmdb_non_media_service(
     ss: &Arc<SupportingService>,
 ) -> Result<NonMediaTmdbService> {
-    Ok(NonMediaTmdbService::new(
-        &ss.config.movies_and_shows.tmdb.access_token,
-        ss.config.movies_and_shows.tmdb.locale.clone(),
-        Arc::new(ss.timezone),
-    )
-    .await)
+    Ok(NonMediaTmdbService::new(ss.clone()).await)
 }
 
 pub async fn get_metadata_provider(
@@ -140,15 +136,10 @@ pub async fn get_metadata_provider(
         MediaSource::Itunes => Box::new(ITunesService::new(&ss.config.podcasts.itunes).await),
         MediaSource::GoogleBooks => Box::new(get_google_books_service(&ss.config).await?),
         MediaSource::Audible => Box::new(AudibleService::new(&ss.config.audio_books.audible).await),
-        MediaSource::Listennotes => Box::new(ListennotesService::new(&ss.config.podcasts).await),
+        MediaSource::Listennotes => Box::new(ListennotesService::new(ss.clone()).await),
         MediaSource::Tmdb => match lot {
-            MediaLot::Show => Box::new(
-                TmdbShowService::new(&ss.config.movies_and_shows.tmdb, Arc::new(ss.timezone)).await,
-            ),
-            MediaLot::Movie => Box::new(
-                TmdbMovieService::new(&ss.config.movies_and_shows.tmdb, Arc::new(ss.timezone))
-                    .await,
-            ),
+            MediaLot::Show => Box::new(TmdbShowService::new(ss.clone()).await),
+            MediaLot::Movie => Box::new(TmdbMovieService::new(ss.clone()).await),
             _ => return err(),
         },
         MediaSource::Anilist => match lot {
@@ -165,7 +156,7 @@ pub async fn get_metadata_provider(
             MediaLot::Manga => Box::new(MalMangaService::new(&ss.config.anime_and_manga.mal).await),
             _ => return err(),
         },
-        MediaSource::Igdb => Box::new(IgdbService::new(&ss.config.video_games).await),
+        MediaSource::Igdb => Box::new(IgdbService::new(ss.clone()).await),
         MediaSource::MangaUpdates => {
             Box::new(MangaUpdatesService::new(&ss.config.anime_and_manga.manga_updates).await)
         }
@@ -914,8 +905,8 @@ pub async fn deploy_background_job(
             ))
             .await?;
         }
-        BackgroundJob::ReEvaluateUserWorkouts => {
-            ss.perform_application_job(ApplicationJob::ReEvaluateUserWorkouts(user_id.to_owned()))
+        BackgroundJob::ReviseUserWorkouts => {
+            ss.perform_application_job(ApplicationJob::ReviseUserWorkouts(user_id.to_owned()))
                 .await?;
         }
     };
@@ -1040,6 +1031,7 @@ pub async fn post_review(
             .await?;
         }
     }
+    mark_entity_as_recently_consumed(user_id, &input.entity_id, input.entity_lot, ss).await?;
     Ok(StringIdObject {
         id: insert.id.unwrap(),
     })
@@ -1259,6 +1251,43 @@ pub async fn handle_after_media_seen_tasks(
     Ok(())
 }
 
+pub async fn mark_entity_as_recently_consumed(
+    user_id: &String,
+    entity_id: &String,
+    entity_lot: EntityLot,
+    ss: &Arc<SupportingService>,
+) -> Result<()> {
+    ss.cache_service
+        .set_with_expiry(
+            ApplicationCacheKey::MetadataRecentlyConsumed {
+                entity_lot,
+                user_id: user_id.to_owned(),
+                entity_id: entity_id.to_owned(),
+            },
+            Some(1),
+            ApplicationCacheValue::Empty,
+        )
+        .await?;
+    Ok(())
+}
+
+pub async fn get_entity_recently_consumed(
+    user_id: &String,
+    entity_id: &String,
+    entity_lot: EntityLot,
+    ss: &Arc<SupportingService>,
+) -> Result<bool> {
+    Ok(ss
+        .cache_service
+        .get_key(ApplicationCacheKey::MetadataRecentlyConsumed {
+            entity_lot,
+            user_id: user_id.to_owned(),
+            entity_id: entity_id.to_owned(),
+        })
+        .await?
+        .is_some())
+}
+
 pub async fn progress_update(
     user_id: &String,
     // update only if media has not been consumed for this user in the last `n` duration
@@ -1276,12 +1305,14 @@ pub async fn progress_update(
         anime_episode_number: input.anime_episode_number,
         podcast_episode_number: input.podcast_episode_number,
     };
-    let in_cache = ss.cache_service.get(cache.clone()).await?;
-    if respect_cache && in_cache.is_some() {
-        ryot_log!(debug, "Seen is already in cache");
-        return Ok(ProgressUpdateResultUnion::Error(ProgressUpdateError {
-            error: ProgressUpdateErrorVariant::AlreadySeen,
-        }));
+    if respect_cache {
+        let in_cache = ss.cache_service.get_key(cache.clone()).await?;
+        if in_cache.is_some() {
+            ryot_log!(debug, "Seen is already in cache");
+            return Ok(ProgressUpdateResultUnion::Error(ProgressUpdateError {
+                error: ProgressUpdateErrorVariant::AlreadySeen,
+            }));
+        }
     }
     ryot_log!(debug, "Input for progress_update = {:?}", input);
 
@@ -1369,7 +1400,10 @@ pub async fn progress_update(
                     }))
             }
 
-            last_seen.update(&ss.db).await.unwrap()
+            let ls = last_seen.update(&ss.db).await.unwrap();
+            mark_entity_as_recently_consumed(user_id, &input.metadata_id, EntityLot::Metadata, ss)
+                .await?;
+            ls
         }
         ProgressUpdateAction::ChangeState => {
             let new_state = input.change_state.unwrap_or(SeenState::Dropped);
@@ -1453,10 +1487,19 @@ pub async fn progress_update(
             };
             ryot_log!(debug, "Progress update finished on = {:?}", finished_on);
             let (progress, mut started_on) = match action {
-                ProgressUpdateAction::JustStarted => (
-                    input.progress.unwrap_or(dec!(0)),
-                    Some(Utc::now().date_naive()),
-                ),
+                ProgressUpdateAction::JustStarted => {
+                    mark_entity_as_recently_consumed(
+                        user_id,
+                        &input.metadata_id,
+                        EntityLot::Metadata,
+                        ss,
+                    )
+                    .await?;
+                    (
+                        input.progress.unwrap_or(dec!(0)),
+                        Some(Utc::now().date_naive()),
+                    )
+                }
                 _ => (dec!(100), None),
             };
             if matches!(action, ProgressUpdateAction::InThePast) && input.start_date.is_some() {
@@ -1484,7 +1527,11 @@ pub async fn progress_update(
     let id = seen.id.clone();
     if seen.state == SeenState::Completed && respect_cache {
         ss.cache_service
-            .set_with_expiry(cache, ss.config.server.progress_update_threshold)
+            .set_with_expiry(
+                cache,
+                Some(ss.config.server.progress_update_threshold),
+                ApplicationCacheValue::Empty,
+            )
             .await?;
     }
     if seen.state == SeenState::Completed {
@@ -1559,16 +1606,14 @@ fn get_index_of_highest_pb(
     record.and_then(|r| records.iter().position(|l| l.statistic == r.statistic))
 }
 
-// DEV: Formula from https://en.wikipedia.org/wiki/One-repetition_maximum#cite_note-7
 fn calculate_one_rm(value: &WorkoutSetRecord) -> Option<Decimal> {
-    let mut val =
-        (value.statistic.weight? * dec!(36.0)).checked_div(dec!(37.0) - value.statistic.reps?);
-    if let Some(v) = val {
-        if v <= dec!(0) {
-            val = None;
-        }
+    let weight = value.statistic.weight?;
+    let reps = value.statistic.reps?;
+    let val = match reps < dec!(10) {
+        true => (weight * dec!(36.0)).checked_div(dec!(37.0) - reps), // Brzycki
+        false => weight.checked_mul((dec!(1).checked_add(reps.checked_div(dec!(30))?))?), // Epley
     };
-    val
+    val.filter(|v| v <= &dec!(0))
 }
 
 fn calculate_volume(value: &WorkoutSetRecord) -> Option<Decimal> {
@@ -1623,7 +1668,7 @@ pub async fn get_focused_workout_summary(
     ss: &Arc<SupportingService>,
 ) -> WorkoutFocusedSummary {
     let db_exercises = Exercise::find()
-        .filter(exercise::Column::Id.is_in(exercises.iter().map(|e| e.name.clone())))
+        .filter(exercise::Column::Id.is_in(exercises.iter().map(|e| e.id.clone())))
         .all(&ss.db)
         .await
         .unwrap();
@@ -1633,7 +1678,7 @@ pub async fn get_focused_workout_summary(
     let mut muscles = HashMap::new();
     let mut equipments = HashMap::new();
     for (idx, ex) in exercises.iter().enumerate() {
-        let exercise = db_exercises.iter().find(|e| e.id == ex.name).unwrap();
+        let exercise = db_exercises.iter().find(|e| e.id == ex.id).unwrap();
         lots.entry(exercise.lot).or_insert(vec![]).push(idx);
         levels.entry(exercise.level).or_insert(vec![]).push(idx);
         if let Some(force) = exercise.force {
@@ -1688,7 +1733,7 @@ pub async fn create_or_update_workout(
     input: UserWorkoutInput,
     user_id: &String,
     ss: &Arc<SupportingService>,
-) -> AnyhowResult<String> {
+) -> Result<String> {
     let end_time = input.end_time;
     let mut input = input;
     let (new_workout_id, to_update_workout) = match &input.update_workout_id {
@@ -1708,7 +1753,7 @@ pub async fn create_or_update_workout(
     let mut exercises = vec![];
     let mut workout_totals = vec![];
     if input.exercises.is_empty() {
-        bail!("This workout has no associated exercises")
+        return Err(Error::new("This workout has no associated exercises"));
     }
     let mut first_set_of_exercise_confirmed_at = input
         .exercises
@@ -1720,7 +1765,7 @@ pub async fn create_or_update_workout(
         .confirmed_at;
     for (exercise_idx, ex) in input.exercises.iter_mut().enumerate() {
         if ex.sets.is_empty() {
-            bail!("This exercise has no associated sets")
+            return Err(Error::new("This exercise has no associated sets"));
         }
         let Some(db_ex) = Exercise::find_by_id(ex.exercise_id.clone())
             .one(&ss.db)
@@ -1902,7 +1947,7 @@ pub async fn create_or_update_workout(
             ProcessedExercise {
                 sets,
                 lot: db_ex.lot,
-                name: db_ex.id,
+                id: db_ex.id,
                 total: Some(totals),
                 notes: ex.notes.clone(),
                 assets: ex.assets.clone(),
@@ -1938,7 +1983,7 @@ pub async fn create_or_update_workout(
                 .map(|(best_set, lot, e)| WorkoutSummaryExercise {
                     best_set,
                     lot: Some(lot),
-                    name: e.name.clone(),
+                    id: e.id.clone(),
                     num_sets: e.sets.len(),
                 })
                 .collect(),
@@ -1962,7 +2007,7 @@ pub async fn create_or_update_workout(
     }
     let data = insert.insert(&ss.db).await?;
     if to_update_workout.is_some() {
-        deploy_job_to_re_evaluate_user_workouts(user_id, ss).await;
+        schedule_user_for_workout_revision(user_id, ss).await?;
     }
     Ok(data.id)
 }
@@ -1999,6 +2044,10 @@ async fn create_collection_and_add_entity_to_it(
     Ok(())
 }
 
+pub fn generate_exercise_id(name: &str, lot: ExerciseLot, user_id: &str) -> String {
+    format!("{}_{}_{}", name, lot, user_id)
+}
+
 pub async fn create_custom_exercise(
     user_id: &String,
     input: exercise::Model,
@@ -2006,6 +2055,7 @@ pub async fn create_custom_exercise(
 ) -> Result<String> {
     let exercise_id = input.id.clone();
     let mut input = input;
+    input.id = generate_exercise_id(&input.name, input.lot, user_id);
     input.created_by_user_id = Some(user_id.clone());
     input.source = ExerciseSource::Custom;
     input.attributes.internal_images = input
@@ -2267,9 +2317,9 @@ where
                 if let Err(err) = create_or_update_workout(workout, user_id, ss).await {
                     import.failed.push(ImportFailedItem {
                         lot: None,
-                        step: ImportFailStep::InputTransformation,
+                        error: Some(err.message),
                         identifier: "Exercise".to_string(),
-                        error: Some(err.to_string()),
+                        step: ImportFailStep::InputTransformation,
                     });
                 }
             }
@@ -2279,9 +2329,9 @@ where
                     Err(err) => {
                         import.failed.push(ImportFailedItem {
                             lot: None,
-                            step: ImportFailStep::InputTransformation,
+                            error: Some(err.message),
                             identifier: "Exercise".to_string(),
-                            error: Some(err.to_string()),
+                            step: ImportFailStep::InputTransformation,
                         });
                     }
                     Ok(workout_id) => {
@@ -2341,7 +2391,7 @@ pub fn db_workout_to_workout_input(user_workout: workout::Model) -> UserWorkoutI
             .exercises
             .into_iter()
             .map(|e| UserExerciseInput {
-                exercise_id: e.name,
+                exercise_id: e.id,
                 sets: e
                     .sets
                     .into_iter()
@@ -2359,3 +2409,72 @@ pub fn db_workout_to_workout_input(user_workout: workout::Model) -> UserWorkoutI
             .collect(),
     }
 }
+
+pub async fn add_entity_to_collection(
+    user_id: &String,
+    input: ChangeCollectionToEntityInput,
+    ss: &Arc<SupportingService>,
+) -> Result<bool> {
+    let collection = Collection::find()
+        .left_join(UserToEntity)
+        .filter(user_to_entity::Column::UserId.eq(user_id))
+        .filter(collection::Column::Name.eq(input.collection_name))
+        .one(&ss.db)
+        .await
+        .unwrap()
+        .unwrap();
+    let mut updated: collection::ActiveModel = collection.into();
+    updated.last_updated_on = ActiveValue::Set(Utc::now());
+    let collection = updated.update(&ss.db).await.unwrap();
+    let column = get_cte_column_from_lot(input.entity_lot);
+    let resp = if let Some(etc) = CollectionToEntity::find()
+        .filter(collection_to_entity::Column::CollectionId.eq(collection.id.clone()))
+        .filter(column.eq(input.entity_id.clone()))
+        .one(&ss.db)
+        .await?
+    {
+        let mut to_update: collection_to_entity::ActiveModel = etc.into();
+        to_update.last_updated_on = ActiveValue::Set(Utc::now());
+        to_update.update(&ss.db).await?
+    } else {
+        let mut created_collection = collection_to_entity::ActiveModel {
+            collection_id: ActiveValue::Set(collection.id),
+            information: ActiveValue::Set(input.information),
+            ..Default::default()
+        };
+        let id = input.entity_id.clone();
+        match input.entity_lot {
+            EntityLot::Metadata => created_collection.metadata_id = ActiveValue::Set(Some(id)),
+            EntityLot::Person => created_collection.person_id = ActiveValue::Set(Some(id)),
+            EntityLot::MetadataGroup => {
+                created_collection.metadata_group_id = ActiveValue::Set(Some(id))
+            }
+            EntityLot::Exercise => created_collection.exercise_id = ActiveValue::Set(Some(id)),
+            EntityLot::Workout => created_collection.workout_id = ActiveValue::Set(Some(id)),
+            EntityLot::WorkoutTemplate => {
+                created_collection.workout_template_id = ActiveValue::Set(Some(id))
+            }
+            EntityLot::Collection | EntityLot::Review | EntityLot::UserMeasurement => {
+                unreachable!()
+            }
+        }
+        let created = created_collection.insert(&ss.db).await?;
+        ryot_log!(debug, "Created collection to entity: {:?}", created);
+        match input.entity_lot {
+            EntityLot::Workout
+            | EntityLot::WorkoutTemplate
+            | EntityLot::Review
+            | EntityLot::UserMeasurement => {}
+            _ => {
+                associate_user_with_entity(&ss.db, user_id, &input.entity_id, input.entity_lot)
+                    .await
+                    .ok();
+            }
+        }
+        created
+    };
+    mark_entity_as_recently_consumed(user_id, &input.entity_id, input.entity_lot, ss).await?;
+    ss.perform_application_job(ApplicationJob::HandleEntityAddedToCollectionEvent(resp.id))
+        .await?;
+    Ok(true)
+}
diff --git a/docs/content/contributing.md b/docs/content/contributing.md
index aedf0e7869..338a489ac1 100644
--- a/docs/content/contributing.md
+++ b/docs/content/contributing.md
@@ -1,12 +1,5 @@
 # Contributing
 
-Each version of Ryot is released as docker images. For example, if the latest tag is
-`v5.2.1`, then the docker image will be tagged as `v5.2.1`, `v5.2`, `v5` and `latest`. The
-images will be made available on [Docker Hub](https://hub.docker.com/r/ignisda/ryot) and
-[GitHub Container Registry](https://ghcr.io/ignisda/ryot).
-
-## Development
-
 There is a [devcontainer](https://code.visualstudio.com/docs/devcontainers/containers)
 configuration in the repository. You can use it to launch a development environment
 with all tools installed.
diff --git a/docs/content/guides/authentication.md b/docs/content/guides/authentication.md
index 712de2074c..5e5e39a74e 100644
--- a/docs/content/guides/authentication.md
+++ b/docs/content/guides/authentication.md
@@ -27,7 +27,8 @@ username set to their email address. This can be changed later in the profile se
 
 !!! warning
 
-      A user can authenticate using only one provider at a time.
+      A user can either have a username/password or it can use your OIDC provider to
+      authenticate but not both.
 
 You can set `USERS_DISABLE_LOCAL_AUTH=true` to disable local authentication and only allow
 users to authenticate using OIDC.
diff --git a/docs/content/guides/books.md b/docs/content/guides/books.md
index 627242dd2a..0533e76835 100644
--- a/docs/content/guides/books.md
+++ b/docs/content/guides/books.md
@@ -29,4 +29,4 @@ Google Books.
 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#important-parameters) docs.
+   [configuration](../configuration.md) docs.
diff --git a/docs/content/importing.md b/docs/content/importing.md
index e85193134c..cb8b9eda2f 100644
--- a/docs/content/importing.md
+++ b/docs/content/importing.md
@@ -19,7 +19,7 @@ You can see the reports under "Import History" of the imports page.
 
 You can import your watched movies and shows from [Jellyfin](https://jellyfin.org).
 
-!!! warning
+!!! info
 
       This will only import media that are already finished. Setup an
       [integration](./integrations.md#jellyfin) if you want to import media in progress.
@@ -31,10 +31,10 @@ whose data you want to import.
 
 You can import your watched movies and shows from [Plex](https://plex.tv).
 
-!!! warning
+!!! info
 
       This will only import media that are already finished. Setup an
-      [integration](./integrations.md#plex) if you want to import media in progress.
+      [integration](./integrations.md#plex-sink) if you want to import media in progress.
 
 ### Steps
 
@@ -67,7 +67,7 @@ their ratings, history, comments and lists. A few points to note.
 The Audiobookshelf importer supports importing all media that have a valid Audible ID or
 ITunes ID or ISBN.
 
-!!! warning
+!!! info
 
     - This will only import media that are already finished. Setup an
       [integration](./integrations.md#audiobookshelf) if you want to import media in progress.
@@ -166,9 +166,8 @@ translates "Read Status" in the following manner:
 ## Strong App
 
 You can import your completed workouts from [Strong](https://www.strong.app/) app. If an
-exercise does not exist in your instance, it will be created with a unique identifier. You
-can later use the "Edit Exercise" or "Merge Exercise" actions to map the exercise to an
-existing one.
+exercise does not exist in your instance, it will be created. You can later use the "Edit
+Exercise" or "Merge Exercise" actions to map the exercise to an existing one.
 
 ### Steps
 
diff --git a/docs/content/index.md b/docs/content/index.md
index d1bfcf9691..57db10f7e7 100644
--- a/docs/content/index.md
+++ b/docs/content/index.md
@@ -51,6 +51,17 @@ If the key is invalid or your subscription has expired, the server will automati
 with the community version. Since the two versions are compatible, you can switch between
 them by simply fixing the key and restarting the server.
 
+## Releases
+
+Each version of Ryot is released as docker images. For example, if the latest tag is
+`v5.2.1`, then the docker image will be tagged as `v5.2.1`, `v5.2`, `v5` and `latest`. The
+images will be made available on [Docker Hub](https://hub.docker.com/r/ignisda/ryot) and
+[GitHub Container Registry](https://ghcr.io/ignisda/ryot).
+
+Ryot is released on a (loosely) weekly basis. If you prefer to live on the edge, you can
+use the `develop` docker tag which is released when changes are merged into the `main`
+branch.
+
 ## Telemetry
 
 Ryot collects anonymous usage data to help me prioritize features. It uses a self-hosted
diff --git a/docs/content/integrations.md b/docs/content/integrations.md
index e5c3ceae0b..6c23e5a903 100644
--- a/docs/content/integrations.md
+++ b/docs/content/integrations.md
@@ -63,7 +63,12 @@ will work for all the media that have a valid TMDb ID attached to their metadata
     if you already have the show in the Ryot database. To do this, simply add the show to
     your watchlist.
 
-### Plex
+### Plex Sink
+
+!!! info
+
+      This will only import media that are in progress. Perform an
+      [import](./importing.md#plex) if you want to import media that are finished.
 
 Automatically add [Plex](https://www.plex.tv/) show and movie plays to Ryot. It will
 work for all the media that have a valid TMDb ID attached to their metadata.
@@ -110,9 +115,12 @@ natively.
 You can configure the interval at which the data is fetched from the external source using
 the `INTEGRATION_SYNC_EVERY_MINUTES` environment variable. Defaults to `5`.
 
+If you have enabled the `Sync to owned collection` option, the integration will also run
+at night to add all media in your instance to your "Owned" collection.
+
 ### Audiobookshelf
 
-!!! warning
+!!! info
 
       This will only import media that are in progress. Perform an
       [import](./importing.md#audiobookshelf) if you want to import media that are finished.
@@ -122,7 +130,7 @@ have a valid provider ID (Audible, ITunes or ISBN).
 
 1. Obtain an API token as described in the Audiobookshelf
    [authentication](https://api.audiobookshelf.org/#authentication) docs.
-2. Go to your Ryot user settings and add the correct details as described in the
+2. Go to your Ryot integrations settings and add the correct details as described in the
    [yank](#yank-integrations) section.
 
 ### Komga
@@ -149,6 +157,19 @@ Then perform these steps on Ryot
 4. Provide your preferred metadata provider. Ryot will attempt the others if the preferred
    is unavailable and will fallback to title search otherwise.
 
+### Plex Yank
+
+This integration will add all media in your libraries to the "Owned" collection. If you
+want to sync media progress, then take a look at the [Plex Sink](#plex-sink) integration.
+
+1. Get the Plex token. If you want to import data for an admin account, then you need a
+   [device
+   token](https://www.plexopedia.com/plex-media-server/general/plex-token/#devicetoken).
+   Otherwise, a current [user
+   token](https://www.plexopedia.com/plex-media-server/general/plex-token/#getcurrentusertoken)
+   is sufficient.
+2. Go to your Ryot integration settings and fill in the details.
+
 ## Push integrations
 
 You can enable the following push integrations:
diff --git a/docs/includes/export-schema.ts b/docs/includes/export-schema.ts
index 4eb11449e0..e03e15ae95 100644
--- a/docs/includes/export-schema.ts
+++ b/docs/includes/export-schema.ts
@@ -275,12 +275,12 @@ export interface WorkoutOrExerciseTotals {
 /** An exercise that has been processed and committed to the database. */
 export interface ProcessedExercise {
 	assets: EntityAssets | null;
+	id: string;
 	/**
 	 * @default 'reps_and_weight'
 	 * @type {'duration' | 'distance_and_duration' | 'reps' | 'reps_and_weight'}
 	 */
 	lot: ExerciseLot;
-	name: string;
 	notes: string[];
 	sets: WorkoutSetRecord[];
 	total: WorkoutOrExerciseTotals | null;
@@ -304,9 +304,9 @@ export interface WorkoutInformation {
 /** The summary about an exercise done in a workout. */
 export interface WorkoutSummaryExercise {
 	best_set: WorkoutSetRecord | null;
+	id: string;
 	/** @default 'reps_and_weight' */
 	lot: ExerciseLot | null;
-	name: string;
 	num_sets: number;
 }
 
diff --git a/libs/generated/src/graphql/backend/gql.ts b/libs/generated/src/graphql/backend/gql.ts
index e9ccbc693b..fb222d3f29 100644
--- a/libs/generated/src/graphql/backend/gql.ts
+++ b/libs/generated/src/graphql/backend/gql.ts
@@ -15,15 +15,15 @@ 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 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: UpdateComplexJsonInput!) {\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}": types.RegisterUserDocument,
-    "query LatestUserSummary {\n  latestUserSummary {\n    totalMetadataReviewCount\n    totalCollectionReviewCount\n    totalMetadataGroupReviewCount\n    totalPersonReviewCount\n    measurementCount\n    workoutCount\n    totalWorkoutDuration\n    audioBookCount\n    totalAudioBookDuration\n    animeCount\n    bookCount\n    totalBookPages\n    podcastCount\n    totalPodcastDuration\n    mangaCount\n    movieCount\n    totalMovieDuration\n    showCount\n    totalShowDuration\n    videoGameCount\n    totalVideoGameDuration\n    visualNovelCount\n    totalVisualNovelDuration\n    totalWorkoutPersonalBests\n    totalWorkoutWeight\n    totalWorkoutReps\n    totalWorkoutDistance\n    totalWorkoutRestTime\n    totalMetadataCount\n    totalReviewCount\n    totalCount\n    totalDuration\n  }\n}": types.LatestUserSummaryDocument,
     "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    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  }\n}": types.MetadataDetailsDocument,
     "query PersonDetails($personId: String!) {\n  personDetails(personId: $personId) {\n    sourceUrl\n    details {\n      id\n      name\n      source\n      identifier\n      isPartial\n      description\n      birthDate\n      deathDate\n      place\n      website\n      gender\n      displayImages\n    }\n    contents {\n      name\n      count\n      items {\n        character\n        metadataId\n      }\n    }\n  }\n}": types.PersonDetailsDocument,
-    "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            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          others {\n            calendar\n            collections\n          }\n          fitness {\n            enabled\n            workouts\n            templates\n            measurements\n          }\n          media {\n            enabled\n            anime\n            audioBook\n            book\n            manga\n            movie\n            podcast\n            show\n            videoGame\n            visualNovel\n            people\n            groups\n            genres\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          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            workoutId\n            exerciseIdx\n            setIdx\n          }\n        }\n      }\n    }\n  }\n}": types.UserExerciseDetailsDocument,
+    "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        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        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            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            anime\n            audioBook\n            book\n            manga\n            movie\n            podcast\n            show\n            videoGame\n            visualNovel\n            people\n            groups\n            genres\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    collections {\n      ...CollectionPart\n    }\n    inProgress {\n      ...SeenPart\n    }\n    history {\n      ...SeenPart\n    }\n    averageRating\n    reviews {\n      ...ReviewItemPart\n    }\n    seenByAllCount\n    seenByUserCount\n    nextEntry {\n      season\n      volume\n      episode\n      chapter\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    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 UserRecommendations {\n  userRecommendations\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 DailyUserActivities($input: DailyUserActivitiesInput!) {\n  dailyUserActivities(input: $input) {\n    groupedBy\n    totalCount\n    totalDuration\n    items {\n      day\n      totalReviewCount\n      workoutCount\n      measurementCount\n      audioBookCount\n      animeCount\n      bookCount\n      podcastCount\n      mangaCount\n      showCount\n      movieCount\n      videoGameCount\n      visualNovelCount\n    }\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    source\n    level\n    force\n    mechanic\n    equipment\n    muscles\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      id\n      lot\n      image\n      muscle\n      numTimesInteracted\n      lastUpdatedOn\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\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    details {\n      total\n      nextPage\n    }\n    items {\n      id\n      name\n      numItems\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    isPro\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    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    sourceUrl\n    details {\n      id\n      title\n      lot\n      source\n      displayImages\n      identifier\n      parts\n      isPartial\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      databaseId\n      hasInteracted\n      item {\n        identifier\n        title\n        image\n        publishYear\n      }\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    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\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      id\n      name\n      endTime\n      duration\n      startTime\n      summary {\n        ...WorkoutSummaryPart\n      }\n    }\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      visibility\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      id\n      name\n      createdOn\n      visibility\n      summary {\n        ...WorkoutSummaryPart\n      }\n    }\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    lot\n    name\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    lot\n    name\n    notes\n    total {\n      ...WorkoutOrExerciseTotalsPart\n    }\n    assets {\n      ...EntityAssetsPart\n    }\n    sets {\n      lot\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}": types.SeenPodcastExtraInformationPartFragmentDoc,
+    "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    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 UserRecommendations {\n  userRecommendations\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    details {\n      total\n      nextPage\n    }\n    items {\n      id\n      lot\n      name\n      image\n      muscle\n      lastUpdatedOn\n      numTimesInteracted\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\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    details {\n      total\n      nextPage\n    }\n    items {\n      id\n      name\n      numItems\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    sourceUrl\n    details {\n      id\n      title\n      lot\n      source\n      displayImages\n      identifier\n      parts\n      isPartial\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      databaseId\n      hasInteracted\n      item {\n        identifier\n        title\n        image\n        publishYear\n      }\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      visibility\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      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}": types.SeenPodcastExtraInformationPartFragmentDoc,
 };
 
 /**
@@ -47,23 +47,23 @@ 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 LatestUserSummary {\n  latestUserSummary {\n    totalMetadataReviewCount\n    totalCollectionReviewCount\n    totalMetadataGroupReviewCount\n    totalPersonReviewCount\n    measurementCount\n    workoutCount\n    totalWorkoutDuration\n    audioBookCount\n    totalAudioBookDuration\n    animeCount\n    bookCount\n    totalBookPages\n    podcastCount\n    totalPodcastDuration\n    mangaCount\n    movieCount\n    totalMovieDuration\n    showCount\n    totalShowDuration\n    videoGameCount\n    totalVideoGameDuration\n    visualNovelCount\n    totalVisualNovelDuration\n    totalWorkoutPersonalBests\n    totalWorkoutWeight\n    totalWorkoutReps\n    totalWorkoutDistance\n    totalWorkoutRestTime\n    totalMetadataCount\n    totalReviewCount\n    totalCount\n    totalDuration\n  }\n}"): (typeof documents)["query LatestUserSummary {\n  latestUserSummary {\n    totalMetadataReviewCount\n    totalCollectionReviewCount\n    totalMetadataGroupReviewCount\n    totalPersonReviewCount\n    measurementCount\n    workoutCount\n    totalWorkoutDuration\n    audioBookCount\n    totalAudioBookDuration\n    animeCount\n    bookCount\n    totalBookPages\n    podcastCount\n    totalPodcastDuration\n    mangaCount\n    movieCount\n    totalMovieDuration\n    showCount\n    totalShowDuration\n    videoGameCount\n    totalVideoGameDuration\n    visualNovelCount\n    totalVisualNovelDuration\n    totalWorkoutPersonalBests\n    totalWorkoutWeight\n    totalWorkoutReps\n    totalWorkoutDistance\n    totalWorkoutRestTime\n    totalMetadataCount\n    totalReviewCount\n    totalCount\n    totalDuration\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    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  }\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    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  }\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 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    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  }\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    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  }\n}"];
+export function graphql(source: "query PersonDetails($personId: String!) {\n  personDetails(personId: $personId) {\n    sourceUrl\n    details {\n      id\n      name\n      source\n      identifier\n      isPartial\n      description\n      birthDate\n      deathDate\n      place\n      website\n      gender\n      displayImages\n    }\n    contents {\n      name\n      count\n      items {\n        character\n        metadataId\n      }\n    }\n  }\n}"): (typeof documents)["query PersonDetails($personId: String!) {\n  personDetails(personId: $personId) {\n    sourceUrl\n    details {\n      id\n      name\n      source\n      identifier\n      isPartial\n      description\n      birthDate\n      deathDate\n      place\n      website\n      gender\n      displayImages\n    }\n    contents {\n      name\n      count\n      items {\n        character\n        metadataId\n      }\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    sourceUrl\n    details {\n      id\n      name\n      source\n      identifier\n      isPartial\n      description\n      birthDate\n      deathDate\n      place\n      website\n      gender\n      displayImages\n    }\n    contents {\n      name\n      count\n      items {\n        character\n        metadataId\n      }\n    }\n  }\n}"): (typeof documents)["query PersonDetails($personId: String!) {\n  personDetails(personId: $personId) {\n    sourceUrl\n    details {\n      id\n      name\n      source\n      identifier\n      isPartial\n      description\n      birthDate\n      deathDate\n      place\n      website\n      gender\n      displayImages\n    }\n    contents {\n      name\n      count\n      items {\n        character\n        metadataId\n      }\n    }\n  }\n}"];
+export function graphql(source: "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        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        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}"): (typeof documents)["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        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        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}"];
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
-export function graphql(source: "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            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          others {\n            calendar\n            collections\n          }\n          fitness {\n            enabled\n            workouts\n            templates\n            measurements\n          }\n          media {\n            enabled\n            anime\n            audioBook\n            book\n            manga\n            movie\n            podcast\n            show\n            videoGame\n            visualNovel\n            people\n            groups\n            genres\n          }\n        }\n      }\n    }\n  }\n}"): (typeof documents)["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            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          others {\n            calendar\n            collections\n          }\n          fitness {\n            enabled\n            workouts\n            templates\n            measurements\n          }\n          media {\n            enabled\n            anime\n            audioBook\n            book\n            manga\n            movie\n            podcast\n            show\n            videoGame\n            visualNovel\n            people\n            groups\n            genres\n          }\n        }\n      }\n    }\n  }\n}"];
+export function graphql(source: "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            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            anime\n            audioBook\n            book\n            manga\n            movie\n            podcast\n            show\n            videoGame\n            visualNovel\n            people\n            groups\n            genres\n          }\n        }\n      }\n    }\n  }\n}"): (typeof documents)["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            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            anime\n            audioBook\n            book\n            manga\n            movie\n            podcast\n            show\n            videoGame\n            visualNovel\n            people\n            groups\n            genres\n          }\n        }\n      }\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 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          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            workoutId\n            exerciseIdx\n            setIdx\n          }\n        }\n      }\n    }\n  }\n}"): (typeof documents)["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          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            workoutId\n            exerciseIdx\n            setIdx\n          }\n        }\n      }\n    }\n  }\n}"];
+export function graphql(source: "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}"): (typeof documents)["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}"];
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
@@ -71,15 +71,15 @@ export function graphql(source: "query UserMeasurementsList($input: UserMeasurem
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
-export function graphql(source: "query UserMetadataDetails($metadataId: String!) {\n  userMetadataDetails(metadataId: $metadataId) {\n    mediaReason\n    hasInteracted\n    collections {\n      ...CollectionPart\n    }\n    inProgress {\n      ...SeenPart\n    }\n    history {\n      ...SeenPart\n    }\n    averageRating\n    reviews {\n      ...ReviewItemPart\n    }\n    seenByAllCount\n    seenByUserCount\n    nextEntry {\n      season\n      volume\n      episode\n      chapter\n    }\n    showProgress {\n      timesSeen\n      seasonNumber\n      episodes {\n        episodeNumber\n        timesSeen\n      }\n    }\n    podcastProgress {\n      episodeNumber\n      timesSeen\n    }\n  }\n}"): (typeof documents)["query UserMetadataDetails($metadataId: String!) {\n  userMetadataDetails(metadataId: $metadataId) {\n    mediaReason\n    hasInteracted\n    collections {\n      ...CollectionPart\n    }\n    inProgress {\n      ...SeenPart\n    }\n    history {\n      ...SeenPart\n    }\n    averageRating\n    reviews {\n      ...ReviewItemPart\n    }\n    seenByAllCount\n    seenByUserCount\n    nextEntry {\n      season\n      volume\n      episode\n      chapter\n    }\n    showProgress {\n      timesSeen\n      seasonNumber\n      episodes {\n        episodeNumber\n        timesSeen\n      }\n    }\n    podcastProgress {\n      episodeNumber\n      timesSeen\n    }\n  }\n}"];
+export function graphql(source: "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}"): (typeof documents)["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}"];
 /**
  * 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    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 UserRecommendations {\n  userRecommendations\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 DailyUserActivities($input: DailyUserActivitiesInput!) {\n  dailyUserActivities(input: $input) {\n    groupedBy\n    totalCount\n    totalDuration\n    items {\n      day\n      totalReviewCount\n      workoutCount\n      measurementCount\n      audioBookCount\n      animeCount\n      bookCount\n      podcastCount\n      mangaCount\n      showCount\n      movieCount\n      videoGameCount\n      visualNovelCount\n    }\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    source\n    level\n    force\n    mechanic\n    equipment\n    muscles\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      id\n      lot\n      image\n      muscle\n      numTimesInteracted\n      lastUpdatedOn\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\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    details {\n      total\n      nextPage\n    }\n    items {\n      id\n      name\n      numItems\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    isPro\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    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    sourceUrl\n    details {\n      id\n      title\n      lot\n      source\n      displayImages\n      identifier\n      parts\n      isPartial\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      databaseId\n      hasInteracted\n      item {\n        identifier\n        title\n        image\n        publishYear\n      }\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    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\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      id\n      name\n      endTime\n      duration\n      startTime\n      summary {\n        ...WorkoutSummaryPart\n      }\n    }\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      visibility\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      id\n      name\n      createdOn\n      visibility\n      summary {\n        ...WorkoutSummaryPart\n      }\n    }\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    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 UserRecommendations {\n  userRecommendations\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 DailyUserActivities($input: DailyUserActivitiesInput!) {\n  dailyUserActivities(input: $input) {\n    groupedBy\n    totalCount\n    totalDuration\n    items {\n      day\n      totalReviewCount\n      workoutCount\n      measurementCount\n      audioBookCount\n      animeCount\n      bookCount\n      podcastCount\n      mangaCount\n      showCount\n      movieCount\n      videoGameCount\n      visualNovelCount\n    }\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    source\n    level\n    force\n    mechanic\n    equipment\n    muscles\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      id\n      lot\n      image\n      muscle\n      numTimesInteracted\n      lastUpdatedOn\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\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    details {\n      total\n      nextPage\n    }\n    items {\n      id\n      name\n      numItems\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    isPro\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    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    sourceUrl\n    details {\n      id\n      title\n      lot\n      source\n      displayImages\n      identifier\n      parts\n      isPartial\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      databaseId\n      hasInteracted\n      item {\n        identifier\n        title\n        image\n        publishYear\n      }\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    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\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      id\n      name\n      endTime\n      duration\n      startTime\n      summary {\n        ...WorkoutSummaryPart\n      }\n    }\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      visibility\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      id\n      name\n      createdOn\n      visibility\n      summary {\n        ...WorkoutSummaryPart\n      }\n    }\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    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 UserRecommendations {\n  userRecommendations\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    details {\n      total\n      nextPage\n    }\n    items {\n      id\n      lot\n      name\n      image\n      muscle\n      lastUpdatedOn\n      numTimesInteracted\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\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    details {\n      total\n      nextPage\n    }\n    items {\n      id\n      name\n      numItems\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    sourceUrl\n    details {\n      id\n      title\n      lot\n      source\n      displayImages\n      identifier\n      parts\n      isPartial\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      databaseId\n      hasInteracted\n      item {\n        identifier\n        title\n        image\n        publishYear\n      }\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      visibility\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    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 UserRecommendations {\n  userRecommendations\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    details {\n      total\n      nextPage\n    }\n    items {\n      id\n      lot\n      name\n      image\n      muscle\n      lastUpdatedOn\n      numTimesInteracted\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\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    details {\n      total\n      nextPage\n    }\n    items {\n      id\n      name\n      numItems\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    sourceUrl\n    details {\n      id\n      title\n      lot\n      source\n      displayImages\n      identifier\n      parts\n      isPartial\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      databaseId\n      hasInteracted\n      item {\n        identifier\n        title\n        image\n        publishYear\n      }\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      visibility\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.
  */
-export function graphql(source: "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    lot\n    name\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    lot\n    name\n    notes\n    total {\n      ...WorkoutOrExerciseTotalsPart\n    }\n    assets {\n      ...EntityAssetsPart\n    }\n    sets {\n      lot\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}"): (typeof documents)["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    lot\n    name\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    lot\n    name\n    notes\n    total {\n      ...WorkoutOrExerciseTotalsPart\n    }\n    assets {\n      ...EntityAssetsPart\n    }\n    sets {\n      lot\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}"];
+export function graphql(source: "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      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}"): (typeof documents)["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      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}"];
 
 export function graphql(source: string) {
   return (documents as any)[source] ?? {};
diff --git a/libs/generated/src/graphql/backend/graphql.ts b/libs/generated/src/graphql/backend/graphql.ts
index d91a1ec5c1..0c257e4489 100644
--- a/libs/generated/src/graphql/backend/graphql.ts
+++ b/libs/generated/src/graphql/backend/graphql.ts
@@ -79,6 +79,18 @@ export type AnimeSpecificsInput = {
   episodes?: InputMaybe<Scalars['Int']['input']>;
 };
 
+/** The start date must be before the end date. */
+export type ApplicationDateRange = {
+  endDate?: Maybe<Scalars['NaiveDate']['output']>;
+  startDate?: Maybe<Scalars['NaiveDate']['output']>;
+};
+
+/** The start date must be before the end date. */
+export type ApplicationDateRangeInput = {
+  endDate?: InputMaybe<Scalars['NaiveDate']['input']>;
+  startDate?: InputMaybe<Scalars['NaiveDate']['input']>;
+};
+
 export type AudioBookSpecifics = {
   runtime?: Maybe<Scalars['Int']['output']>;
 };
@@ -104,7 +116,7 @@ export enum BackgroundJob {
   CalculateUserActivitiesAndSummary = 'CALCULATE_USER_ACTIVITIES_AND_SUMMARY',
   PerformBackgroundTasks = 'PERFORM_BACKGROUND_TASKS',
   RecalculateCalendarEvents = 'RECALCULATE_CALENDAR_EVENTS',
-  ReEvaluateUserWorkouts = 'RE_EVALUATE_USER_WORKOUTS',
+  ReviseUserWorkouts = 'REVISE_USER_WORKOUTS',
   SyncIntegrationsData = 'SYNC_INTEGRATIONS_DATA',
   UpdateAllExercises = 'UPDATE_ALL_EXERCISES',
   UpdateAllMetadata = 'UPDATE_ALL_METADATA'
@@ -223,7 +235,7 @@ export type CoreDetails = {
   exerciseParameters: ExerciseParameters;
   fileStorageEnabled: Scalars['Boolean']['output'];
   frontend: FrontendConfig;
-  isPro: Scalars['Boolean']['output'];
+  isServerKeyValidated: Scalars['Boolean']['output'];
   localAuthDisabled: Scalars['Boolean']['output'];
   metadataLotSourceMappings: Array<MetadataLotSourceMappings>;
   metadataProviderLanguages: Array<ProviderLanguageInformation>;
@@ -320,12 +332,6 @@ export type CreateUserNotificationPlatformInput = {
   priority?: InputMaybe<Scalars['Int']['input']>;
 };
 
-export type DailyUserActivitiesInput = {
-  endDate?: InputMaybe<Scalars['NaiveDate']['input']>;
-  groupBy?: InputMaybe<DailyUserActivitiesResponseGroupedBy>;
-  startDate?: InputMaybe<Scalars['NaiveDate']['input']>;
-};
-
 export type DailyUserActivitiesResponse = {
   groupedBy: DailyUserActivitiesResponseGroupedBy;
   itemCount: Scalars['Int']['output'];
@@ -341,13 +347,23 @@ export enum DailyUserActivitiesResponseGroupedBy {
   Year = 'YEAR'
 }
 
+export type DailyUserActivityHourRecord = {
+  entities: Array<DailyUserActivityHourRecordEntity>;
+  hour: Scalars['Int']['output'];
+};
+
+export type DailyUserActivityHourRecordEntity = {
+  entityId: Scalars['String']['output'];
+  entityLot: EntityLot;
+  metadataLot?: Maybe<MediaLot>;
+};
+
 export type DailyUserActivityItem = {
   animeCount: Scalars['Int']['output'];
   audioBookCount: Scalars['Int']['output'];
   bookCount: Scalars['Int']['output'];
   day: Scalars['NaiveDate']['output'];
   mangaCount: Scalars['Int']['output'];
-  measurementCount: Scalars['Int']['output'];
   movieCount: Scalars['Int']['output'];
   podcastCount: Scalars['Int']['output'];
   showCount: Scalars['Int']['output'];
@@ -372,13 +388,13 @@ export type DailyUserActivityItem = {
   totalWorkoutReps: Scalars['Int']['output'];
   totalWorkoutRestTime: Scalars['Int']['output'];
   totalWorkoutWeight: Scalars['Int']['output'];
+  userMeasurementCount: Scalars['Int']['output'];
   videoGameCount: Scalars['Int']['output'];
   visualNovelCount: Scalars['Int']['output'];
   workoutCount: Scalars['Int']['output'];
 };
 
 export enum DashboardElementLot {
-  Activity = 'ACTIVITY',
   InProgress = 'IN_PROGRESS',
   Recommendations = 'RECOMMENDATIONS',
   Summary = 'SUMMARY',
@@ -487,6 +503,7 @@ export type Exercise = {
   lot: ExerciseLot;
   mechanic?: Maybe<ExerciseMechanic>;
   muscles: Array<ExerciseMuscle>;
+  name: Scalars['String']['output'];
   source: ExerciseSource;
 };
 
@@ -540,11 +557,11 @@ export type ExerciseInput = {
   attributes: ExerciseAttributesInput;
   equipment?: InputMaybe<ExerciseEquipment>;
   force?: InputMaybe<ExerciseForce>;
-  id: Scalars['String']['input'];
   level: ExerciseLevel;
   lot: ExerciseLot;
   mechanic?: InputMaybe<ExerciseMechanic>;
   muscles: Array<ExerciseMuscle>;
+  name: Scalars['String']['input'];
 };
 
 export enum ExerciseLevel {
@@ -569,6 +586,7 @@ export type ExerciseListItem = {
   lastUpdatedOn?: Maybe<Scalars['DateTime']['output']>;
   lot: ExerciseLot;
   muscle?: Maybe<ExerciseMuscle>;
+  name: Scalars['String']['output'];
   numTimesInteracted?: Maybe<Scalars['Int']['output']>;
 };
 
@@ -651,6 +669,21 @@ export type ExternalIdentifiers = {
   tvdbId?: Maybe<Scalars['Int']['output']>;
 };
 
+export type FitnessAnalyticsEquipment = {
+  count: Scalars['Int']['output'];
+  equipment: ExerciseEquipment;
+};
+
+export type FitnessAnalyticsExercise = {
+  count: Scalars['Int']['output'];
+  exercise: Scalars['String']['output'];
+};
+
+export type FitnessAnalyticsMuscle = {
+  count: Scalars['Int']['output'];
+  muscle: ExerciseMuscle;
+};
+
 export type FrontendConfig = {
   /** A message to be displayed on the dashboard. */
   dashboardMessage: Scalars['String']['output'];
@@ -875,7 +908,8 @@ export enum IntegrationProvider {
   JellyfinSink = 'JELLYFIN_SINK',
   Kodi = 'KODI',
   Komga = 'KOMGA',
-  Plex = 'PLEX',
+  PlexSink = 'PLEX_SINK',
+  PlexYank = 'PLEX_YANK',
   Radarr = 'RADARR',
   Sonarr = 'SONARR'
 }
@@ -890,7 +924,9 @@ export type IntegrationSourceSpecificsInput = {
   komgaPassword?: InputMaybe<Scalars['String']['input']>;
   komgaProvider?: InputMaybe<MediaSource>;
   komgaUsername?: InputMaybe<Scalars['String']['input']>;
-  plexUsername?: InputMaybe<Scalars['String']['input']>;
+  plexSinkUsername?: InputMaybe<Scalars['String']['input']>;
+  plexYankBaseUrl?: InputMaybe<Scalars['String']['input']>;
+  plexYankToken?: InputMaybe<Scalars['String']['input']>;
   radarrApiKey?: InputMaybe<Scalars['String']['input']>;
   radarrBaseUrl?: InputMaybe<Scalars['String']['input']>;
   radarrProfileId?: InputMaybe<Scalars['Int']['input']>;
@@ -1668,8 +1704,8 @@ export type ProcessAccessLinkResult = ProcessAccessLinkError | ProcessAccessLink
 /** An exercise that has been processed and committed to the database. */
 export type ProcessedExercise = {
   assets?: Maybe<EntityAssets>;
+  id: Scalars['String']['output'];
   lot: ExerciseLot;
-  name: Scalars['String']['output'];
   notes: Array<Scalars['String']['output']>;
   sets: Array<WorkoutSetRecord>;
   total?: Maybe<WorkoutOrExerciseTotals>;
@@ -1700,8 +1736,6 @@ export type QueryRoot = {
   collectionContents: CollectionContents;
   /** Get some primary information about the service. */
   coreDetails: CoreDetails;
-  /** Get daily user activities for the currently logged in user. */
-  dailyUserActivities: DailyUserActivitiesResponse;
   /** Get details about an exercise. */
   exerciseDetails: Exercise;
   /** Get a paginated list of exercises in the database. */
@@ -1718,8 +1752,6 @@ export type QueryRoot = {
   getPresignedS3Url: Scalars['String']['output'];
   /** Get all the import jobs deployed by the user. */
   importReports: Array<ImportReport>;
-  /** Get a summary of all the media items that have been consumed by this user. */
-  latestUserSummary: DailyUserActivityItem;
   /** Get details about a media present in the database. */
   metadataDetails: GraphqlMetadataDetails;
   /** Get details about a metadata group present in the database. */
@@ -1742,6 +1774,10 @@ export type QueryRoot = {
   personDetails: PersonDetails;
   /** Get all access links generated by the currently logged in user. */
   userAccessLinks: Array<AccessLink>;
+  /** Get the analytics for the currently logged in user. */
+  userAnalytics: UserAnalytics;
+  /** Get the analytics parameters for the currently logged in user. */
+  userAnalyticsParameters: ApplicationDateRange;
   /** Get user by OIDC issuer ID. */
   userByOidcIssuerId?: Maybe<Scalars['String']['output']>;
   /** Get calendar events for a user between a given date range. */
@@ -1775,9 +1811,9 @@ export type QueryRoot = {
   /** Get information about a workout template. */
   userWorkoutTemplateDetails: UserWorkoutTemplateDetails;
   /** Get a paginated list of templates created by the user. */
-  userWorkoutTemplatesList: WorkoutTemplateListResults;
+  userWorkoutTemplatesList: IdResults;
   /** Get a paginated list of workouts done by the user. */
-  userWorkoutsList: WorkoutListResults;
+  userWorkoutsList: IdResults;
   /** Get details about all the users in the service. */
   usersList: Array<User>;
 };
@@ -1788,11 +1824,6 @@ export type QueryRootCollectionContentsArgs = {
 };
 
 
-export type QueryRootDailyUserActivitiesArgs = {
-  input: DailyUserActivitiesInput;
-};
-
-
 export type QueryRootExerciseDetailsArgs = {
   exerciseId: Scalars['String']['input'];
 };
@@ -1873,6 +1904,11 @@ export type QueryRootPersonDetailsArgs = {
 };
 
 
+export type QueryRootUserAnalyticsArgs = {
+  input: UserAnalyticsInput;
+};
+
+
 export type QueryRootUserByOidcIssuerIdArgs = {
   oidcIssuerId: Scalars['String']['input'];
 };
@@ -2135,12 +2171,12 @@ export type UpdateCustomExerciseInput = {
   attributes: ExerciseAttributesInput;
   equipment?: InputMaybe<ExerciseEquipment>;
   force?: InputMaybe<ExerciseForce>;
-  id: Scalars['String']['input'];
   level: ExerciseLevel;
   lot: ExerciseLot;
   mechanic?: InputMaybe<ExerciseMechanic>;
   muscles: Array<ExerciseMuscle>;
-  oldName: Scalars['String']['input'];
+  name: Scalars['String']['input'];
+  oldId: Scalars['String']['input'];
   shouldDelete?: InputMaybe<Scalars['Boolean']['input']>;
 };
 
@@ -2160,7 +2196,6 @@ export type UpdateUserExerciseSettings = {
 
 export type UpdateUserInput = {
   adminAccessToken?: InputMaybe<Scalars['String']['input']>;
-  extraInformation?: InputMaybe<Scalars['JSON']['input']>;
   isDisabled?: InputMaybe<Scalars['Boolean']['input']>;
   lot?: InputMaybe<UserLot>;
   password?: InputMaybe<Scalars['String']['input']>;
@@ -2197,6 +2232,21 @@ export type User = {
   preferences: UserPreferences;
 };
 
+export type UserAnalytics = {
+  activities: DailyUserActivitiesResponse;
+  fitness: UserFitnessAnalytics;
+  hours: Array<DailyUserActivityHourRecord>;
+};
+
+export type UserAnalyticsFeaturesEnabledPreferences = {
+  enabled: Scalars['Boolean']['output'];
+};
+
+export type UserAnalyticsInput = {
+  dateRange: ApplicationDateRangeInput;
+  groupBy?: InputMaybe<DailyUserActivitiesResponseGroupedBy>;
+};
+
 export type UserCalendarEventInput = {
   month: Scalars['Int']['input'];
   year: Scalars['Int']['input'];
@@ -2236,11 +2286,26 @@ export type UserExerciseInput = {
 };
 
 export type UserFeaturesEnabledPreferences = {
+  analytics: UserAnalyticsFeaturesEnabledPreferences;
   fitness: UserFitnessFeaturesEnabledPreferences;
   media: UserMediaFeaturesEnabledPreferences;
   others: UserOthersFeaturesEnabledPreferences;
 };
 
+export type UserFitnessAnalytics = {
+  measurementCount: Scalars['Int']['output'];
+  workoutCount: Scalars['Int']['output'];
+  workoutDistance: Scalars['Int']['output'];
+  workoutDuration: Scalars['Int']['output'];
+  workoutEquipments: Array<FitnessAnalyticsEquipment>;
+  workoutExercises: Array<FitnessAnalyticsExercise>;
+  workoutMuscles: Array<FitnessAnalyticsMuscle>;
+  workoutPersonalBests: Scalars['Int']['output'];
+  workoutReps: Scalars['Int']['output'];
+  workoutRestTime: Scalars['Int']['output'];
+  workoutWeight: Scalars['Int']['output'];
+};
+
 export type UserFitnessExercisesPreferences = {
   setRestTimers: SetRestTimersSettings;
   unitSystem: UserUnitSystem;
@@ -2451,6 +2516,8 @@ export type UserMetadataDetails = {
   nextEntry?: Maybe<UserMediaNextEntry>;
   /** The seen progress of this media if it is a podcast. */
   podcastProgress?: Maybe<Array<UserMetadataDetailsEpisodeProgress>>;
+  /** Whether this media has been recently interacted with */
+  recentlyConsumed: Scalars['Boolean']['output'];
   /** The public reviews of this media. */
   reviews: Array<ReviewItem>;
   /** The number of users who have seen this media. */
@@ -2474,6 +2541,7 @@ export type UserMetadataDetailsShowSeasonProgress = {
 
 export type UserMetadataGroupDetails = {
   collections: Array<Collection>;
+  recentlyConsumed: Scalars['Boolean']['output'];
   reviews: Array<ReviewItem>;
 };
 
@@ -2489,6 +2557,7 @@ export type UserOthersFeaturesEnabledPreferences = {
 
 export type UserPersonDetails = {
   collections: Array<Collection>;
+  recentlyConsumed: Scalars['Boolean']['output'];
   reviews: Array<ReviewItem>;
 };
 
@@ -2538,6 +2607,7 @@ export type UserToExerciseHistoryExtraInformation = {
 };
 
 export type UserToExerciseSettingsExtraInformation = {
+  excludeFromAnalytics: Scalars['Boolean']['output'];
   setRestTimers: SetRestTimersSettings;
 };
 
@@ -2667,11 +2737,6 @@ export type WorkoutLevelFocusedSummary = {
   level: ExerciseLevel;
 };
 
-export type WorkoutListResults = {
-  details: SearchDetails;
-  items: Array<Workout>;
-};
-
 export type WorkoutLotFocusedSummary = {
   exercises: Array<Scalars['Int']['output']>;
   lot: ExerciseLot;
@@ -2740,8 +2805,8 @@ export type WorkoutSummary = {
 /** The summary about an exercise done in a workout. */
 export type WorkoutSummaryExercise = {
   bestSet?: Maybe<WorkoutSetRecord>;
+  id: Scalars['String']['output'];
   lot?: Maybe<ExerciseLot>;
-  name: Scalars['String']['output'];
   numSets: Scalars['Int']['output'];
 };
 
@@ -2768,11 +2833,6 @@ export type WorkoutTemplate = {
   visibility: Visibility;
 };
 
-export type WorkoutTemplateListResults = {
-  details: SearchDetails;
-  items: Array<WorkoutTemplate>;
-};
-
 export type RegisterUserMutationVariables = Exact<{
   input: RegisterUserInput;
 }>;
@@ -3126,11 +3186,6 @@ export type MergeExerciseMutationVariables = Exact<{
 
 export type MergeExerciseMutation = { mergeExercise: boolean };
 
-export type LatestUserSummaryQueryVariables = Exact<{ [key: string]: never; }>;
-
-
-export type LatestUserSummaryQuery = { latestUserSummary: { totalMetadataReviewCount: number, totalCollectionReviewCount: number, totalMetadataGroupReviewCount: number, totalPersonReviewCount: number, measurementCount: number, workoutCount: number, totalWorkoutDuration: number, audioBookCount: number, totalAudioBookDuration: number, animeCount: number, bookCount: number, totalBookPages: number, podcastCount: number, totalPodcastDuration: number, mangaCount: number, movieCount: number, totalMovieDuration: number, showCount: number, totalShowDuration: number, videoGameCount: number, totalVideoGameDuration: number, visualNovelCount: number, totalVisualNovelDuration: number, totalWorkoutPersonalBests: number, totalWorkoutWeight: number, totalWorkoutReps: number, totalWorkoutDistance: number, totalWorkoutRestTime: number, totalMetadataCount: number, totalReviewCount: number, totalCount: number, totalDuration: number } };
-
 export type MetadataDetailsQueryVariables = Exact<{
   metadataId: Scalars['String']['input'];
 }>;
@@ -3145,17 +3200,24 @@ export type PersonDetailsQueryVariables = Exact<{
 
 export type PersonDetailsQuery = { personDetails: { sourceUrl?: string | null, details: { id: string, name: string, source: MediaSource, identifier: string, isPartial?: boolean | null, description?: string | null, birthDate?: string | null, deathDate?: string | null, place?: string | null, website?: string | null, gender?: string | null, displayImages: Array<string> }, contents: Array<{ name: string, count: number, items: Array<{ character?: string | null, metadataId: string }> }> } };
 
+export type UserAnalyticsQueryVariables = Exact<{
+  input: UserAnalyticsInput;
+}>;
+
+
+export type UserAnalyticsQuery = { userAnalytics: { hours: Array<{ hour: number, entities: Array<{ entityId: string, entityLot: EntityLot, metadataLot?: MediaLot | null }> }>, activities: { groupedBy: DailyUserActivitiesResponseGroupedBy, totalCount: number, totalDuration: number, items: Array<{ day: string, bookCount: number, showCount: number, totalCount: number, movieCount: number, animeCount: number, mangaCount: number, workoutCount: number, podcastCount: number, totalDuration: number, totalBookPages: number, audioBookCount: number, videoGameCount: number, totalReviewCount: number, visualNovelCount: number, totalWorkoutReps: number, totalShowDuration: number, totalMovieDuration: number, totalMetadataCount: number, totalWorkoutWeight: number, totalPodcastDuration: number, totalWorkoutDistance: number, totalWorkoutRestTime: number, totalWorkoutDuration: number, userMeasurementCount: number, totalVideoGameDuration: number, totalAudioBookDuration: number, totalPersonReviewCount: number, totalVisualNovelDuration: number, totalMetadataReviewCount: number, totalWorkoutPersonalBests: number, totalCollectionReviewCount: number, totalMetadataGroupReviewCount: number }> }, fitness: { workoutReps: number, workoutCount: number, workoutWeight: number, workoutDistance: number, workoutDuration: number, workoutRestTime: number, measurementCount: number, workoutPersonalBests: number, workoutExercises: Array<{ count: number, exercise: string }>, workoutMuscles: Array<{ count: number, muscle: ExerciseMuscle }>, workoutEquipments: Array<{ count: number, equipment: ExerciseEquipment }> } } };
+
 export type UserDetailsQueryVariables = Exact<{ [key: string]: never; }>;
 
 
-export type UserDetailsQuery = { userDetails: { __typename: 'User', id: string, lot: UserLot, name: string, isDisabled?: boolean | null, oidcIssuerId?: string | null, preferences: { general: { reviewScale: UserReviewScale, gridPacking: GridPacking, displayNsfw: boolean, disableVideos: boolean, persistQueries: boolean, disableReviews: boolean, disableIntegrations: boolean, disableWatchProviders: boolean, disableNavigationAnimation: boolean, dashboard: Array<{ hidden: boolean, section: DashboardElementLot, numElements?: number | null, deduplicateMedia?: boolean | null }>, watchProviders: Array<{ lot: MediaLot, values: Array<string> }> }, fitness: { logging: { muteSounds: boolean, showDetailsWhileEditing: boolean }, exercises: { unitSystem: UserUnitSystem, setRestTimers: { drop?: number | null, warmup?: number | null, normal?: number | null, failure?: number | null } }, measurements: { custom: Array<{ name: string, dataType: UserCustomMeasurementDataType }>, inbuilt: { weight: boolean, bodyMassIndex: boolean, totalBodyWater: boolean, muscle: boolean, leanBodyMass: boolean, bodyFat: boolean, boneMass: boolean, visceralFat: boolean, waistCircumference: boolean, waistToHeightRatio: boolean, hipCircumference: boolean, waistToHipRatio: boolean, chestCircumference: boolean, thighCircumference: boolean, bicepsCircumference: boolean, neckCircumference: boolean, bodyFatCaliper: boolean, chestSkinfold: boolean, abdominalSkinfold: boolean, thighSkinfold: boolean, basalMetabolicRate: boolean, totalDailyEnergyExpenditure: boolean, calories: boolean } } }, notifications: { toSend: Array<MediaStateChanged>, enabled: boolean }, featuresEnabled: { others: { calendar: boolean, collections: boolean }, fitness: { enabled: boolean, workouts: boolean, templates: boolean, measurements: boolean }, media: { enabled: boolean, anime: boolean, audioBook: boolean, book: boolean, manga: boolean, movie: boolean, podcast: boolean, show: boolean, videoGame: boolean, visualNovel: boolean, people: boolean, groups: boolean, genres: boolean } } } } | { __typename: 'UserDetailsError' } };
+export type UserDetailsQuery = { userDetails: { __typename: 'User', id: string, lot: UserLot, name: string, isDisabled?: boolean | null, oidcIssuerId?: string | null, preferences: { general: { reviewScale: UserReviewScale, gridPacking: GridPacking, displayNsfw: boolean, disableVideos: boolean, persistQueries: boolean, disableReviews: boolean, disableIntegrations: boolean, disableWatchProviders: boolean, disableNavigationAnimation: boolean, dashboard: Array<{ hidden: boolean, section: DashboardElementLot, numElements?: number | null, deduplicateMedia?: boolean | null }>, watchProviders: Array<{ lot: MediaLot, values: Array<string> }> }, fitness: { logging: { muteSounds: boolean, showDetailsWhileEditing: boolean }, exercises: { unitSystem: UserUnitSystem, setRestTimers: { drop?: number | null, warmup?: number | null, normal?: number | null, failure?: number | null } }, measurements: { custom: Array<{ name: string, dataType: UserCustomMeasurementDataType }>, inbuilt: { weight: boolean, bodyMassIndex: boolean, totalBodyWater: boolean, muscle: boolean, leanBodyMass: boolean, bodyFat: boolean, boneMass: boolean, visceralFat: boolean, waistCircumference: boolean, waistToHeightRatio: boolean, hipCircumference: boolean, waistToHipRatio: boolean, chestCircumference: boolean, thighCircumference: boolean, bicepsCircumference: boolean, neckCircumference: boolean, bodyFatCaliper: boolean, chestSkinfold: boolean, abdominalSkinfold: boolean, thighSkinfold: boolean, basalMetabolicRate: boolean, totalDailyEnergyExpenditure: boolean, calories: boolean } } }, notifications: { toSend: Array<MediaStateChanged>, enabled: boolean }, featuresEnabled: { analytics: { enabled: boolean }, others: { calendar: boolean, collections: boolean }, fitness: { enabled: boolean, workouts: boolean, templates: boolean, measurements: boolean }, media: { enabled: boolean, anime: boolean, audioBook: boolean, book: boolean, manga: boolean, movie: boolean, podcast: boolean, show: boolean, videoGame: boolean, visualNovel: boolean, people: boolean, groups: boolean, genres: boolean } } } } | { __typename: 'UserDetailsError' } };
 
 export type UserExerciseDetailsQueryVariables = Exact<{
   exerciseId: Scalars['String']['input'];
 }>;
 
 
-export type UserExerciseDetailsQuery = { userExerciseDetails: { collections: Array<{ id: string, name: string, userId: string }>, reviews: Array<{ id: string, rating?: string | null, postedOn: string, isSpoiler: boolean, visibility: Visibility, textOriginal?: string | null, textRendered?: string | null, seenItemsAssociatedWith: Array<string>, postedBy: { id: string, name: string }, comments: Array<{ id: string, text: string, likedBy: Array<string>, createdOn: string, user: { id: string, name: string } }>, showExtraInformation?: { season?: number | null, episode?: number | null } | null, podcastExtraInformation?: { episode?: number | null } | null, animeExtraInformation?: { episode?: number | null } | null, mangaExtraInformation?: { volume?: number | null, chapter?: string | null } | null }>, history?: Array<{ idx: number, workoutId: string, workoutEndOn: string, bestSet?: { lot: SetLot, personalBests?: Array<WorkoutSetPersonalBest> | null, statistic: { reps?: string | null, pace?: string | null, oneRm?: string | null, weight?: string | null, volume?: string | null, duration?: string | null, distance?: string | null } } | null }> | null, details?: { exerciseId?: string | null, createdOn: string, lastUpdatedOn: string, exerciseNumTimesInteracted?: number | null, exerciseExtraInformation?: { settings: { setRestTimers: { drop?: number | null, warmup?: number | null, normal?: number | null, failure?: number | null } }, lifetimeStats: { weight: string, reps: string, distance: string, duration: string, personalBestsAchieved: number }, personalBests: Array<{ lot: WorkoutSetPersonalBest, sets: Array<{ workoutId: string, exerciseIdx: number, setIdx: number }> }> } | null } | null } };
+export type UserExerciseDetailsQuery = { userExerciseDetails: { collections: Array<{ id: string, name: string, userId: string }>, reviews: Array<{ id: string, rating?: string | null, postedOn: string, isSpoiler: boolean, visibility: Visibility, textOriginal?: string | null, textRendered?: string | null, seenItemsAssociatedWith: Array<string>, postedBy: { id: string, name: string }, comments: Array<{ id: string, text: string, likedBy: Array<string>, createdOn: string, user: { id: string, name: string } }>, showExtraInformation?: { season?: number | null, episode?: number | null } | null, podcastExtraInformation?: { episode?: number | null } | null, animeExtraInformation?: { episode?: number | null } | null, mangaExtraInformation?: { volume?: number | null, chapter?: string | null } | null }>, history?: Array<{ idx: number, workoutId: string, workoutEndOn: string, bestSet?: { lot: SetLot, personalBests?: Array<WorkoutSetPersonalBest> | null, statistic: { reps?: string | null, pace?: string | null, oneRm?: string | null, weight?: string | null, volume?: string | null, duration?: string | null, distance?: string | null } } | null }> | null, details?: { exerciseId?: string | null, createdOn: string, lastUpdatedOn: string, exerciseNumTimesInteracted?: number | null, exerciseExtraInformation?: { settings: { excludeFromAnalytics: boolean, setRestTimers: { drop?: number | null, warmup?: number | null, normal?: number | null, failure?: number | null } }, lifetimeStats: { weight: string, reps: string, distance: string, duration: string, personalBestsAchieved: number }, personalBests: Array<{ lot: WorkoutSetPersonalBest, sets: Array<{ setIdx: number, workoutId: string, exerciseIdx: number }> }> } | null } | null } };
 
 export type UserMeasurementsListQueryVariables = Exact<{
   input: UserMeasurementsListInput;
@@ -3169,7 +3231,7 @@ export type UserMetadataDetailsQueryVariables = Exact<{
 }>;
 
 
-export type UserMetadataDetailsQuery = { userMetadataDetails: { mediaReason?: Array<UserToMediaReason> | null, hasInteracted: boolean, averageRating?: string | null, seenByAllCount: number, seenByUserCount: number, collections: Array<{ id: string, name: string, userId: string }>, inProgress?: { id: string, state: SeenState, progress: string, reviewId?: string | null, startedOn?: string | null, finishedOn?: string | null, lastUpdatedOn: string, manualTimeSpent?: string | null, numTimesUpdated: number, providerWatchedOn?: string | null, showExtraInformation?: { episode: number, season: number } | null, podcastExtraInformation?: { episode: number } | null, animeExtraInformation?: { episode?: number | null } | null, mangaExtraInformation?: { volume?: number | null, chapter?: string | null } | null } | null, history: Array<{ id: string, state: SeenState, progress: string, reviewId?: string | null, startedOn?: string | null, finishedOn?: string | null, lastUpdatedOn: string, manualTimeSpent?: string | null, numTimesUpdated: number, providerWatchedOn?: string | null, showExtraInformation?: { episode: number, season: number } | null, podcastExtraInformation?: { episode: number } | null, animeExtraInformation?: { episode?: number | null } | null, mangaExtraInformation?: { volume?: number | null, chapter?: string | null } | null }>, reviews: Array<{ id: string, rating?: string | null, postedOn: string, isSpoiler: boolean, visibility: Visibility, textOriginal?: string | null, textRendered?: string | null, seenItemsAssociatedWith: Array<string>, postedBy: { id: string, name: string }, comments: Array<{ id: string, text: string, likedBy: Array<string>, createdOn: string, user: { id: string, name: string } }>, showExtraInformation?: { season?: number | null, episode?: number | null } | null, podcastExtraInformation?: { episode?: number | null } | null, animeExtraInformation?: { episode?: number | null } | null, mangaExtraInformation?: { volume?: number | null, chapter?: string | null } | null }>, nextEntry?: { season?: number | null, volume?: number | null, episode?: number | null, chapter?: string | null } | null, showProgress?: Array<{ timesSeen: number, seasonNumber: number, episodes: Array<{ episodeNumber: number, timesSeen: number }> }> | null, podcastProgress?: Array<{ episodeNumber: number, timesSeen: number }> | null } };
+export type UserMetadataDetailsQuery = { userMetadataDetails: { mediaReason?: Array<UserToMediaReason> | null, hasInteracted: boolean, averageRating?: string | null, seenByAllCount: number, seenByUserCount: number, recentlyConsumed: boolean, reviews: Array<{ id: string, rating?: string | null, postedOn: string, isSpoiler: boolean, visibility: Visibility, textOriginal?: string | null, textRendered?: string | null, seenItemsAssociatedWith: Array<string>, postedBy: { id: string, name: string }, comments: Array<{ id: string, text: string, likedBy: Array<string>, createdOn: string, user: { id: string, name: string } }>, showExtraInformation?: { season?: number | null, episode?: number | null } | null, podcastExtraInformation?: { episode?: number | null } | null, animeExtraInformation?: { episode?: number | null } | null, mangaExtraInformation?: { volume?: number | null, chapter?: string | null } | null }>, history: Array<{ id: string, state: SeenState, progress: string, reviewId?: string | null, startedOn?: string | null, finishedOn?: string | null, lastUpdatedOn: string, manualTimeSpent?: string | null, numTimesUpdated: number, providerWatchedOn?: string | null, showExtraInformation?: { episode: number, season: number } | null, podcastExtraInformation?: { episode: number } | null, animeExtraInformation?: { episode?: number | null } | null, mangaExtraInformation?: { volume?: number | null, chapter?: string | null } | null }>, nextEntry?: { season?: number | null, volume?: number | null, episode?: number | null, chapter?: string | null } | null, inProgress?: { id: string, state: SeenState, progress: string, reviewId?: string | null, startedOn?: string | null, finishedOn?: string | null, lastUpdatedOn: string, manualTimeSpent?: string | null, numTimesUpdated: number, providerWatchedOn?: string | null, showExtraInformation?: { episode: number, season: number } | null, podcastExtraInformation?: { episode: number } | null, animeExtraInformation?: { episode?: number | null } | null, mangaExtraInformation?: { volume?: number | null, chapter?: string | null } | null } | null, collections: Array<{ id: string, name: string, userId: string }>, showProgress?: Array<{ timesSeen: number, seasonNumber: number, episodes: Array<{ episodeNumber: number, timesSeen: number }> }> | null, podcastProgress?: Array<{ episodeNumber: number, timesSeen: number }> | null } };
 
 export type GetOidcRedirectUrlQueryVariables = Exact<{ [key: string]: never; }>;
 
@@ -3271,26 +3333,19 @@ export type UserAccessLinksQueryVariables = Exact<{ [key: string]: never; }>;
 
 export type UserAccessLinksQuery = { userAccessLinks: Array<{ id: string, name: string, isDemo?: boolean | null, createdOn: string, expiresOn?: string | null, timesUsed: number, isRevoked?: boolean | null, maximumUses?: number | null, isAccountDefault?: boolean | null, isMutationAllowed?: boolean | null }> };
 
-export type DailyUserActivitiesQueryVariables = Exact<{
-  input: DailyUserActivitiesInput;
-}>;
-
-
-export type DailyUserActivitiesQuery = { dailyUserActivities: { groupedBy: DailyUserActivitiesResponseGroupedBy, totalCount: number, totalDuration: number, items: Array<{ day: string, totalReviewCount: number, workoutCount: number, measurementCount: number, audioBookCount: number, animeCount: number, bookCount: number, podcastCount: number, mangaCount: number, showCount: number, movieCount: number, videoGameCount: number, visualNovelCount: number }> } };
-
 export type ExerciseDetailsQueryVariables = Exact<{
   exerciseId: Scalars['String']['input'];
 }>;
 
 
-export type ExerciseDetailsQuery = { exerciseDetails: { id: string, lot: ExerciseLot, source: ExerciseSource, level: ExerciseLevel, force?: ExerciseForce | null, mechanic?: ExerciseMechanic | null, equipment?: ExerciseEquipment | null, muscles: Array<ExerciseMuscle>, createdByUserId?: string | null, attributes: { instructions: Array<string>, images: Array<string> } } };
+export type ExerciseDetailsQuery = { exerciseDetails: { id: string, lot: ExerciseLot, name: string, level: ExerciseLevel, force?: ExerciseForce | null, source: ExerciseSource, muscles: Array<ExerciseMuscle>, mechanic?: ExerciseMechanic | null, equipment?: ExerciseEquipment | null, createdByUserId?: string | null, attributes: { instructions: Array<string>, images: Array<string> } } };
 
 export type ExercisesListQueryVariables = Exact<{
   input: ExercisesListInput;
 }>;
 
 
-export type ExercisesListQuery = { exercisesList: { details: { total: number, nextPage?: number | null }, items: Array<{ id: string, lot: ExerciseLot, image?: string | null, muscle?: ExerciseMuscle | null, numTimesInteracted?: number | null, lastUpdatedOn?: string | null }> } };
+export type ExercisesListQuery = { exercisesList: { details: { total: number, nextPage?: number | null }, items: Array<{ id: string, lot: ExerciseLot, name: string, image?: string | null, muscle?: ExerciseMuscle | null, lastUpdatedOn?: string | null, numTimesInteracted?: number | null }> } };
 
 export type ImportReportsQueryVariables = Exact<{ [key: string]: never; }>;
 
@@ -3321,7 +3376,7 @@ export type CollectionContentsQuery = { collectionContents: { user: { id: string
 export type CoreDetailsQueryVariables = Exact<{ [key: string]: never; }>;
 
 
-export type CoreDetailsQuery = { coreDetails: { isPro: boolean, version: string, docsLink: string, pageSize: number, websiteUrl: string, smtpEnabled: boolean, oidcEnabled: boolean, signupAllowed: boolean, repositoryLink: string, disableTelemetry: boolean, tokenValidForDays: number, localAuthDisabled: boolean, fileStorageEnabled: 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, 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 MetadataGroupDetailsQueryVariables = Exact<{
   metadataGroupId: Scalars['String']['input'];
@@ -3363,42 +3418,47 @@ export type UserMetadataGroupDetailsQueryVariables = Exact<{
 }>;
 
 
-export type UserMetadataGroupDetailsQuery = { userMetadataGroupDetails: { reviews: Array<{ id: string, rating?: string | null, postedOn: string, isSpoiler: boolean, visibility: Visibility, textOriginal?: string | null, textRendered?: string | null, seenItemsAssociatedWith: Array<string>, postedBy: { id: string, name: string }, comments: Array<{ id: string, text: string, likedBy: Array<string>, createdOn: string, user: { id: string, name: string } }>, showExtraInformation?: { season?: number | null, episode?: number | null } | null, podcastExtraInformation?: { episode?: number | null } | null, animeExtraInformation?: { episode?: number | null } | null, mangaExtraInformation?: { volume?: number | null, chapter?: string | null } | null }>, collections: Array<{ id: string, name: string, userId: string }> } };
+export type UserMetadataGroupDetailsQuery = { userMetadataGroupDetails: { recentlyConsumed: boolean, reviews: Array<{ id: string, rating?: string | null, postedOn: string, isSpoiler: boolean, visibility: Visibility, textOriginal?: string | null, textRendered?: string | null, seenItemsAssociatedWith: Array<string>, postedBy: { id: string, name: string }, comments: Array<{ id: string, text: string, likedBy: Array<string>, createdOn: string, user: { id: string, name: string } }>, showExtraInformation?: { season?: number | null, episode?: number | null } | null, podcastExtraInformation?: { episode?: number | null } | null, animeExtraInformation?: { episode?: number | null } | null, mangaExtraInformation?: { volume?: number | null, chapter?: string | null } | null }>, collections: Array<{ id: string, name: string, userId: string }> } };
 
 export type UserPersonDetailsQueryVariables = Exact<{
   personId: Scalars['String']['input'];
 }>;
 
 
-export type UserPersonDetailsQuery = { userPersonDetails: { collections: Array<{ id: string, name: string, userId: string }>, reviews: Array<{ id: string, rating?: string | null, postedOn: string, isSpoiler: boolean, visibility: Visibility, textOriginal?: string | null, textRendered?: string | null, seenItemsAssociatedWith: Array<string>, postedBy: { id: string, name: string }, comments: Array<{ id: string, text: string, likedBy: Array<string>, createdOn: string, user: { id: string, name: string } }>, showExtraInformation?: { season?: number | null, episode?: number | null } | null, podcastExtraInformation?: { episode?: number | null } | null, animeExtraInformation?: { episode?: number | null } | null, mangaExtraInformation?: { volume?: number | null, chapter?: string | null } | null }> } };
+export type UserPersonDetailsQuery = { userPersonDetails: { recentlyConsumed: boolean, collections: Array<{ id: string, name: string, userId: string }>, reviews: Array<{ id: string, rating?: string | null, postedOn: string, isSpoiler: boolean, visibility: Visibility, textOriginal?: string | null, textRendered?: string | null, seenItemsAssociatedWith: Array<string>, postedBy: { id: string, name: string }, comments: Array<{ id: string, text: string, likedBy: Array<string>, createdOn: string, user: { id: string, name: string } }>, showExtraInformation?: { season?: number | null, episode?: number | null } | null, podcastExtraInformation?: { episode?: number | null } | null, animeExtraInformation?: { episode?: number | null } | null, mangaExtraInformation?: { volume?: number | null, chapter?: string | null } | null }> } };
 
 export type UserWorkoutDetailsQueryVariables = Exact<{
   workoutId: Scalars['String']['input'];
 }>;
 
 
-export type UserWorkoutDetailsQuery = { userWorkoutDetails: { collections: Array<{ id: string, name: string, userId: string }>, details: { id: string, name: string, endTime: string, duration: number, startTime: string, templateId?: string | null, repeatedFrom?: string | null, summary: { total?: { reps: string, weight: string, distance: string, duration: string, restTime: number, personalBestsAchieved: number } | null, exercises: Array<{ lot?: ExerciseLot | null, name: string, numSets: number, bestSet?: { lot: SetLot, personalBests?: Array<WorkoutSetPersonalBest> | null, statistic: { reps?: string | null, pace?: string | null, oneRm?: string | null, weight?: string | null, volume?: string | null, duration?: string | null, distance?: string | null } } | null }>, focused: { lots: Array<{ lot: ExerciseLot, exercises: Array<number> }>, levels: Array<{ level: ExerciseLevel, exercises: Array<number> }>, forces: Array<{ force: ExerciseForce, exercises: Array<number> }>, muscles: Array<{ muscle: ExerciseMuscle, exercises: Array<number> }>, equipments: Array<{ equipment: ExerciseEquipment, exercises: Array<number> }> } }, information: { comment?: string | null, assets?: { images: Array<string>, videos: Array<string> } | null, supersets: Array<{ color: string, exercises: Array<number> }>, exercises: Array<{ lot: ExerciseLot, name: string, notes: Array<string>, total?: { reps: string, weight: string, distance: string, duration: string, restTime: number, personalBestsAchieved: number } | null, assets?: { images: Array<string>, videos: Array<string> } | null, sets: Array<{ lot: SetLot, note?: string | null, restTime?: number | null, confirmedAt?: string | null, personalBests?: Array<WorkoutSetPersonalBest> | null, statistic: { reps?: string | null, pace?: string | null, oneRm?: string | null, weight?: string | null, volume?: string | null, duration?: string | null, distance?: string | null } }> }> } } } };
+export type UserWorkoutDetailsQuery = { userWorkoutDetails: { collections: Array<{ id: string, name: string, userId: string }>, details: { id: string, name: string, endTime: string, duration: number, startTime: string, templateId?: string | null, repeatedFrom?: string | null, summary: { total?: { reps: string, weight: string, distance: string, duration: string, restTime: number, personalBestsAchieved: number } | null, exercises: Array<{ id: string, lot?: ExerciseLot | null, numSets: number, bestSet?: { lot: SetLot, personalBests?: Array<WorkoutSetPersonalBest> | null, statistic: { reps?: string | null, pace?: string | null, oneRm?: string | null, weight?: string | null, volume?: string | null, duration?: string | null, distance?: string | null } } | null }>, focused: { lots: Array<{ lot: ExerciseLot, exercises: Array<number> }>, levels: Array<{ level: ExerciseLevel, exercises: Array<number> }>, forces: Array<{ force: ExerciseForce, exercises: Array<number> }>, muscles: Array<{ muscle: ExerciseMuscle, exercises: Array<number> }>, equipments: Array<{ equipment: ExerciseEquipment, exercises: Array<number> }> } }, information: { comment?: string | null, assets?: { images: Array<string>, videos: Array<string> } | null, supersets: Array<{ color: string, exercises: Array<number> }>, exercises: Array<{ id: string, lot: ExerciseLot, notes: Array<string>, total?: { reps: string, weight: string, distance: string, duration: string, restTime: number, personalBestsAchieved: number } | null, assets?: { images: Array<string>, videos: Array<string> } | null, sets: Array<{ lot: SetLot, note?: string | null, restTime?: number | null, confirmedAt?: string | null, personalBests?: Array<WorkoutSetPersonalBest> | null, statistic: { reps?: string | null, pace?: string | null, oneRm?: string | null, weight?: string | null, volume?: string | null, duration?: string | null, distance?: string | null } }> }> } } } };
 
 export type UserWorkoutsListQueryVariables = Exact<{
   input: SearchInput;
 }>;
 
 
-export type UserWorkoutsListQuery = { userWorkoutsList: { details: { total: number, nextPage?: number | null }, items: Array<{ id: string, name: string, endTime: string, duration: number, startTime: string, summary: { total?: { reps: string, weight: string, distance: string, duration: string, restTime: number, personalBestsAchieved: number } | null, exercises: Array<{ lot?: ExerciseLot | null, name: string, numSets: number, bestSet?: { lot: SetLot, personalBests?: Array<WorkoutSetPersonalBest> | null, statistic: { reps?: string | null, pace?: string | null, oneRm?: string | null, weight?: string | null, volume?: string | null, duration?: string | null, distance?: string | null } } | null }>, focused: { lots: Array<{ lot: ExerciseLot, exercises: Array<number> }>, levels: Array<{ level: ExerciseLevel, exercises: Array<number> }>, forces: Array<{ force: ExerciseForce, exercises: Array<number> }>, muscles: Array<{ muscle: ExerciseMuscle, exercises: Array<number> }>, equipments: Array<{ equipment: ExerciseEquipment, exercises: Array<number> }> } } }> } };
+export type UserWorkoutsListQuery = { userWorkoutsList: { items: Array<string>, details: { total: number, nextPage?: number | null } } };
 
 export type UserWorkoutTemplateDetailsQueryVariables = Exact<{
   workoutTemplateId: Scalars['String']['input'];
 }>;
 
 
-export type UserWorkoutTemplateDetailsQuery = { userWorkoutTemplateDetails: { collections: Array<{ id: string, name: string, userId: string }>, details: { id: string, name: string, createdOn: string, visibility: Visibility, summary: { total?: { reps: string, weight: string, distance: string, duration: string, restTime: number, personalBestsAchieved: number } | null, exercises: Array<{ lot?: ExerciseLot | null, name: string, numSets: number, bestSet?: { lot: SetLot, personalBests?: Array<WorkoutSetPersonalBest> | null, statistic: { reps?: string | null, pace?: string | null, oneRm?: string | null, weight?: string | null, volume?: string | null, duration?: string | null, distance?: string | null } } | null }>, focused: { lots: Array<{ lot: ExerciseLot, exercises: Array<number> }>, levels: Array<{ level: ExerciseLevel, exercises: Array<number> }>, forces: Array<{ force: ExerciseForce, exercises: Array<number> }>, muscles: Array<{ muscle: ExerciseMuscle, exercises: Array<number> }>, equipments: Array<{ equipment: ExerciseEquipment, exercises: Array<number> }> } }, information: { comment?: string | null, assets?: { images: Array<string>, videos: Array<string> } | null, supersets: Array<{ color: string, exercises: Array<number> }>, exercises: Array<{ lot: ExerciseLot, name: string, notes: Array<string>, total?: { reps: string, weight: string, distance: string, duration: string, restTime: number, personalBestsAchieved: number } | null, assets?: { images: Array<string>, videos: Array<string> } | null, sets: Array<{ lot: SetLot, note?: string | null, restTime?: number | null, confirmedAt?: string | null, personalBests?: Array<WorkoutSetPersonalBest> | null, statistic: { reps?: string | null, pace?: string | null, oneRm?: string | null, weight?: string | null, volume?: string | null, duration?: string | null, distance?: string | null } }> }> } } } };
+export type UserWorkoutTemplateDetailsQuery = { userWorkoutTemplateDetails: { collections: Array<{ id: string, name: string, userId: string }>, details: { id: string, name: string, createdOn: string, visibility: Visibility, summary: { total?: { reps: string, weight: string, distance: string, duration: string, restTime: number, personalBestsAchieved: number } | null, exercises: Array<{ id: string, lot?: ExerciseLot | null, numSets: number, bestSet?: { lot: SetLot, personalBests?: Array<WorkoutSetPersonalBest> | null, statistic: { reps?: string | null, pace?: string | null, oneRm?: string | null, weight?: string | null, volume?: string | null, duration?: string | null, distance?: string | null } } | null }>, focused: { lots: Array<{ lot: ExerciseLot, exercises: Array<number> }>, levels: Array<{ level: ExerciseLevel, exercises: Array<number> }>, forces: Array<{ force: ExerciseForce, exercises: Array<number> }>, muscles: Array<{ muscle: ExerciseMuscle, exercises: Array<number> }>, equipments: Array<{ equipment: ExerciseEquipment, exercises: Array<number> }> } }, information: { comment?: string | null, assets?: { images: Array<string>, videos: Array<string> } | null, supersets: Array<{ color: string, exercises: Array<number> }>, exercises: Array<{ id: string, lot: ExerciseLot, notes: Array<string>, total?: { reps: string, weight: string, distance: string, duration: string, restTime: number, personalBestsAchieved: number } | null, assets?: { images: Array<string>, videos: Array<string> } | null, sets: Array<{ lot: SetLot, note?: string | null, restTime?: number | null, confirmedAt?: string | null, personalBests?: Array<WorkoutSetPersonalBest> | null, statistic: { reps?: string | null, pace?: string | null, oneRm?: string | null, weight?: string | null, volume?: string | null, duration?: string | null, distance?: string | null } }> }> } } } };
 
 export type UserWorkoutTemplatesListQueryVariables = Exact<{
   input: SearchInput;
 }>;
 
 
-export type UserWorkoutTemplatesListQuery = { userWorkoutTemplatesList: { details: { total: number, nextPage?: number | null }, items: Array<{ id: string, name: string, createdOn: string, visibility: Visibility, summary: { total?: { reps: string, weight: string, distance: string, duration: string, restTime: number, personalBestsAchieved: number } | null, exercises: Array<{ lot?: ExerciseLot | null, name: string, numSets: number, bestSet?: { lot: SetLot, personalBests?: Array<WorkoutSetPersonalBest> | null, statistic: { reps?: string | null, pace?: string | null, oneRm?: string | null, weight?: string | null, volume?: string | null, duration?: string | null, distance?: string | null } } | null }>, focused: { lots: Array<{ lot: ExerciseLot, exercises: Array<number> }>, levels: Array<{ level: ExerciseLevel, exercises: Array<number> }>, forces: Array<{ force: ExerciseForce, exercises: Array<number> }>, muscles: Array<{ muscle: ExerciseMuscle, exercises: Array<number> }>, equipments: Array<{ equipment: ExerciseEquipment, exercises: Array<number> }> } } }> } };
+export type UserWorkoutTemplatesListQuery = { userWorkoutTemplatesList: { items: Array<string>, details: { total: number, nextPage?: number | null } } };
+
+export type UserAnalyticsParametersQueryVariables = Exact<{ [key: string]: never; }>;
+
+
+export type UserAnalyticsParametersQuery = { userAnalyticsParameters: { endDate?: string | null, startDate?: string | null } };
 
 export type SeenPodcastExtraInformationPartFragment = { episode: number };
 
@@ -3422,13 +3482,13 @@ export type WorkoutSetStatisticPartFragment = { reps?: string | null, pace?: str
 
 export type WorkoutSetRecordPartFragment = { lot: SetLot, personalBests?: Array<WorkoutSetPersonalBest> | null, statistic: { reps?: string | null, pace?: string | null, oneRm?: string | null, weight?: string | null, volume?: string | null, duration?: string | null, distance?: string | null } };
 
-export type WorkoutSummaryPartFragment = { total?: { reps: string, weight: string, distance: string, duration: string, restTime: number, personalBestsAchieved: number } | null, exercises: Array<{ lot?: ExerciseLot | null, name: string, numSets: number, bestSet?: { lot: SetLot, personalBests?: Array<WorkoutSetPersonalBest> | null, statistic: { reps?: string | null, pace?: string | null, oneRm?: string | null, weight?: string | null, volume?: string | null, duration?: string | null, distance?: string | null } } | null }>, focused: { lots: Array<{ lot: ExerciseLot, exercises: Array<number> }>, levels: Array<{ level: ExerciseLevel, exercises: Array<number> }>, forces: Array<{ force: ExerciseForce, exercises: Array<number> }>, muscles: Array<{ muscle: ExerciseMuscle, exercises: Array<number> }>, equipments: Array<{ equipment: ExerciseEquipment, exercises: Array<number> }> } };
+export type WorkoutSummaryPartFragment = { total?: { reps: string, weight: string, distance: string, duration: string, restTime: number, personalBestsAchieved: number } | null, exercises: Array<{ id: string, lot?: ExerciseLot | null, numSets: number, bestSet?: { lot: SetLot, personalBests?: Array<WorkoutSetPersonalBest> | null, statistic: { reps?: string | null, pace?: string | null, oneRm?: string | null, weight?: string | null, volume?: string | null, duration?: string | null, distance?: string | null } } | null }>, focused: { lots: Array<{ lot: ExerciseLot, exercises: Array<number> }>, levels: Array<{ level: ExerciseLevel, exercises: Array<number> }>, forces: Array<{ force: ExerciseForce, exercises: Array<number> }>, muscles: Array<{ muscle: ExerciseMuscle, exercises: Array<number> }>, equipments: Array<{ equipment: ExerciseEquipment, exercises: Array<number> }> } };
 
 export type CollectionPartFragment = { id: string, name: string, userId: string };
 
 export type ReviewItemPartFragment = { id: string, rating?: string | null, postedOn: string, isSpoiler: boolean, visibility: Visibility, textOriginal?: string | null, textRendered?: string | null, seenItemsAssociatedWith: Array<string>, postedBy: { id: string, name: string }, comments: Array<{ id: string, text: string, likedBy: Array<string>, createdOn: string, user: { id: string, name: string } }>, showExtraInformation?: { season?: number | null, episode?: number | null } | null, podcastExtraInformation?: { episode?: number | null } | null, animeExtraInformation?: { episode?: number | null } | null, mangaExtraInformation?: { volume?: number | null, chapter?: string | null } | null };
 
-export type WorkoutInformationPartFragment = { comment?: string | null, assets?: { images: Array<string>, videos: Array<string> } | null, supersets: Array<{ color: string, exercises: Array<number> }>, exercises: Array<{ lot: ExerciseLot, name: string, notes: Array<string>, total?: { reps: string, weight: string, distance: string, duration: string, restTime: number, personalBestsAchieved: number } | null, assets?: { images: Array<string>, videos: Array<string> } | null, sets: Array<{ lot: SetLot, note?: string | null, restTime?: number | null, confirmedAt?: string | null, personalBests?: Array<WorkoutSetPersonalBest> | null, statistic: { reps?: string | null, pace?: string | null, oneRm?: string | null, weight?: string | null, volume?: string | null, duration?: string | null, distance?: string | null } }> }> };
+export type WorkoutInformationPartFragment = { comment?: string | null, assets?: { images: Array<string>, videos: Array<string> } | null, supersets: Array<{ color: string, exercises: Array<number> }>, exercises: Array<{ id: string, lot: ExerciseLot, notes: Array<string>, total?: { reps: string, weight: string, distance: string, duration: string, restTime: number, personalBestsAchieved: number } | null, assets?: { images: Array<string>, videos: Array<string> } | null, sets: Array<{ lot: SetLot, note?: string | null, restTime?: number | null, confirmedAt?: string | null, personalBests?: Array<WorkoutSetPersonalBest> | null, statistic: { reps?: string | null, pace?: string | null, oneRm?: string | null, weight?: string | null, volume?: string | null, duration?: string | null, distance?: string | null } }> }> };
 
 export type SetRestTimersPartFragment = { drop?: number | null, warmup?: number | null, normal?: number | null, failure?: number | null };
 
@@ -3442,11 +3502,11 @@ export const MetadataSearchItemPartFragmentDoc = {"kind":"Document","definitions
 export const WorkoutOrExerciseTotalsPartFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutOrExerciseTotals"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"personalBestsAchieved"}}]}}]} as unknown as DocumentNode<WorkoutOrExerciseTotalsPartFragment, unknown>;
 export const WorkoutSetStatisticPartFragmentDoc = {"kind":"Document","definitions":[{"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"}}]}}]} as unknown as DocumentNode<WorkoutSetStatisticPartFragment, unknown>;
 export const WorkoutSetRecordPartFragmentDoc = {"kind":"Document","definitions":[{"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":"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"}}]}}]} as unknown as DocumentNode<WorkoutSetRecordPartFragment, unknown>;
-export const WorkoutSummaryPartFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutSummaryPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSummary"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"numSets"}},{"kind":"Field","name":{"kind":"Name","value":"bestSet"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetRecordPart"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"focused"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lots"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"levels"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"level"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"forces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"force"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"muscles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"muscle"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"equipments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"equipment"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}}]}}]}},{"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":"WorkoutOrExerciseTotalsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutOrExerciseTotals"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"personalBestsAchieved"}}]}},{"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"}}]}}]}}]} as unknown as DocumentNode<WorkoutSummaryPartFragment, unknown>;
+export const WorkoutSummaryPartFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutSummaryPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSummary"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"numSets"}},{"kind":"Field","name":{"kind":"Name","value":"bestSet"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetRecordPart"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"focused"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lots"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"levels"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"level"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"forces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"force"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"muscles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"muscle"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"equipments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"equipment"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}}]}}]}},{"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":"WorkoutOrExerciseTotalsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutOrExerciseTotals"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"personalBestsAchieved"}}]}},{"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"}}]}}]}}]} as unknown as DocumentNode<WorkoutSummaryPartFragment, unknown>;
 export const CollectionPartFragmentDoc = {"kind":"Document","definitions":[{"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"}}]}}]} as unknown as DocumentNode<CollectionPartFragment, unknown>;
 export const ReviewItemPartFragmentDoc = {"kind":"Document","definitions":[{"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":"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"}}]}}]} as unknown as DocumentNode<ReviewItemPartFragment, unknown>;
 export const EntityAssetsPartFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"EntityAssetsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EntityAssets"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"images"}},{"kind":"Field","name":{"kind":"Name","value":"videos"}}]}}]} as unknown as DocumentNode<EntityAssetsPartFragment, unknown>;
-export const WorkoutInformationPartFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EntityAssetsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"supersets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"notes"}},{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EntityAssetsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"note"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"confirmedAt"}},{"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":"EntityAssetsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EntityAssets"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"images"}},{"kind":"Field","name":{"kind":"Name","value":"videos"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutOrExerciseTotals"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"personalBestsAchieved"}}]}},{"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"}}]}}]} as unknown as DocumentNode<WorkoutInformationPartFragment, unknown>;
+export const WorkoutInformationPartFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EntityAssetsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"supersets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"notes"}},{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EntityAssetsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"note"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"confirmedAt"}},{"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":"EntityAssetsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EntityAssets"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"images"}},{"kind":"Field","name":{"kind":"Name","value":"videos"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutOrExerciseTotals"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"personalBestsAchieved"}}]}},{"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"}}]}}]} as unknown as DocumentNode<WorkoutInformationPartFragment, unknown>;
 export const SetRestTimersPartFragmentDoc = {"kind":"Document","definitions":[{"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<SetRestTimersPartFragment, unknown>;
 export const RegisterUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RegisterUser"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RegisterUserInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"registerUser"},"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":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RegisterError"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"error"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StringIdObject"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode<RegisterUserMutation, RegisterUserMutationVariables>;
 export const LoginUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"LoginUser"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AuthUserInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"loginUser"},"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":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LoginError"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"error"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LoginResponse"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apiKey"}}]}}]}}]}}]} as unknown as DocumentNode<LoginUserMutation, LoginUserMutationVariables>;
@@ -3499,13 +3559,13 @@ export const ProcessAccessLinkDocument = {"kind":"Document","definitions":[{"kin
 export const RevokeAccessLinkDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RevokeAccessLink"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"accessLinkId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"revokeAccessLink"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"accessLinkId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"accessLinkId"}}}]}]}}]} as unknown as DocumentNode<RevokeAccessLinkMutation, RevokeAccessLinkMutationVariables>;
 export const UpdateUserExerciseSettingsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateUserExerciseSettings"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateUserExerciseSettings"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateUserExerciseSettings"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<UpdateUserExerciseSettingsMutation, UpdateUserExerciseSettingsMutationVariables>;
 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 LatestUserSummaryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LatestUserSummary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"latestUserSummary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalMetadataReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalCollectionReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalMetadataGroupReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalPersonReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"measurementCount"}},{"kind":"Field","name":{"kind":"Name","value":"workoutCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutDuration"}},{"kind":"Field","name":{"kind":"Name","value":"audioBookCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalAudioBookDuration"}},{"kind":"Field","name":{"kind":"Name","value":"animeCount"}},{"kind":"Field","name":{"kind":"Name","value":"bookCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalBookPages"}},{"kind":"Field","name":{"kind":"Name","value":"podcastCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalPodcastDuration"}},{"kind":"Field","name":{"kind":"Name","value":"mangaCount"}},{"kind":"Field","name":{"kind":"Name","value":"movieCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalMovieDuration"}},{"kind":"Field","name":{"kind":"Name","value":"showCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalShowDuration"}},{"kind":"Field","name":{"kind":"Name","value":"videoGameCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalVideoGameDuration"}},{"kind":"Field","name":{"kind":"Name","value":"visualNovelCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalVisualNovelDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutPersonalBests"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutWeight"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutReps"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutDistance"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutRestTime"}},{"kind":"Field","name":{"kind":"Name","value":"totalMetadataCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalDuration"}}]}}]}}]} as unknown as DocumentNode<LatestUserSummaryQuery, LatestUserSummaryQueryVariables>;
 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":"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"}}]}}]}}]}}]} 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":"sourceUrl"}},{"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":"source"}},{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"isPartial"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"birthDate"}},{"kind":"Field","name":{"kind":"Name","value":"deathDate"}},{"kind":"Field","name":{"kind":"Name","value":"place"}},{"kind":"Field","name":{"kind":"Name","value":"website"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"displayImages"}}]}},{"kind":"Field","name":{"kind":"Name","value":"contents"},"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":"character"}},{"kind":"Field","name":{"kind":"Name","value":"metadataId"}}]}}]}}]}}]}}]} as unknown as DocumentNode<PersonDetailsQuery, PersonDetailsQueryVariables>;
-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":"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":"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":"anime"}},{"kind":"Field","name":{"kind":"Name","value":"audioBook"}},{"kind":"Field","name":{"kind":"Name","value":"book"}},{"kind":"Field","name":{"kind":"Name","value":"manga"}},{"kind":"Field","name":{"kind":"Name","value":"movie"}},{"kind":"Field","name":{"kind":"Name","value":"podcast"}},{"kind":"Field","name":{"kind":"Name","value":"show"}},{"kind":"Field","name":{"kind":"Name","value":"videoGame"}},{"kind":"Field","name":{"kind":"Name","value":"visualNovel"}},{"kind":"Field","name":{"kind":"Name","value":"people"}},{"kind":"Field","name":{"kind":"Name","value":"groups"}},{"kind":"Field","name":{"kind":"Name","value":"genres"}}]}}]}}]}}]}}]}}]}},{"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":"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":"workoutId"}},{"kind":"Field","name":{"kind":"Name","value":"exerciseIdx"}},{"kind":"Field","name":{"kind":"Name","value":"setIdx"}}]}}]}}]}}]}}]}}]}},{"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>;
+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":"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":"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":"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":"anime"}},{"kind":"Field","name":{"kind":"Name","value":"audioBook"}},{"kind":"Field","name":{"kind":"Name","value":"book"}},{"kind":"Field","name":{"kind":"Name","value":"manga"}},{"kind":"Field","name":{"kind":"Name","value":"movie"}},{"kind":"Field","name":{"kind":"Name","value":"podcast"}},{"kind":"Field","name":{"kind":"Name","value":"show"}},{"kind":"Field","name":{"kind":"Name","value":"videoGame"}},{"kind":"Field","name":{"kind":"Name","value":"visualNovel"}},{"kind":"Field","name":{"kind":"Name","value":"people"}},{"kind":"Field","name":{"kind":"Name","value":"groups"}},{"kind":"Field","name":{"kind":"Name","value":"genres"}}]}}]}}]}}]}}]}}]}},{"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>;
 export const UserMeasurementsListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserMeasurementsList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UserMeasurementsListInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userMeasurementsList"},"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":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"stats"},"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":"custom"}}]}}]}}]}}]} as unknown as DocumentNode<UserMeasurementsListQuery, UserMeasurementsListQueryVariables>;
-export const UserMetadataDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserMetadataDetails"},"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":"userMetadataDetails"},"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":"mediaReason"}},{"kind":"Field","name":{"kind":"Name","value":"hasInteracted"}},{"kind":"Field","name":{"kind":"Name","value":"collections"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CollectionPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"inProgress"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"history"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"averageRating"}},{"kind":"Field","name":{"kind":"Name","value":"reviews"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ReviewItemPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"seenByAllCount"}},{"kind":"Field","name":{"kind":"Name","value":"seenByUserCount"}},{"kind":"Field","name":{"kind":"Name","value":"nextEntry"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"volume"}},{"kind":"Field","name":{"kind":"Name","value":"episode"}},{"kind":"Field","name":{"kind":"Name","value":"chapter"}}]}},{"kind":"Field","name":{"kind":"Name","value":"showProgress"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timesSeen"}},{"kind":"Field","name":{"kind":"Name","value":"seasonNumber"}},{"kind":"Field","name":{"kind":"Name","value":"episodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodeNumber"}},{"kind":"Field","name":{"kind":"Name","value":"timesSeen"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastProgress"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodeNumber"}},{"kind":"Field","name":{"kind":"Name","value":"timesSeen"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenShowExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenShowExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}},{"kind":"Field","name":{"kind":"Name","value":"season"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenPodcastExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenPodcastExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"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":"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":"SeenPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Seen"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"progress"}},{"kind":"Field","name":{"kind":"Name","value":"reviewId"}},{"kind":"Field","name":{"kind":"Name","value":"startedOn"}},{"kind":"Field","name":{"kind":"Name","value":"finishedOn"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedOn"}},{"kind":"Field","name":{"kind":"Name","value":"manualTimeSpent"}},{"kind":"Field","name":{"kind":"Name","value":"numTimesUpdated"}},{"kind":"Field","name":{"kind":"Name","value":"providerWatchedOn"}},{"kind":"Field","name":{"kind":"Name","value":"showExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenShowExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenPodcastExtraInformationPart"}}]}},{"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":"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<UserMetadataDetailsQuery, UserMetadataDetailsQueryVariables>;
+export const UserMetadataDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserMetadataDetails"},"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":"userMetadataDetails"},"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":"mediaReason"}},{"kind":"Field","name":{"kind":"Name","value":"hasInteracted"}},{"kind":"Field","name":{"kind":"Name","value":"averageRating"}},{"kind":"Field","name":{"kind":"Name","value":"seenByAllCount"}},{"kind":"Field","name":{"kind":"Name","value":"seenByUserCount"}},{"kind":"Field","name":{"kind":"Name","value":"recentlyConsumed"}},{"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":"FragmentSpread","name":{"kind":"Name","value":"SeenPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"nextEntry"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"volume"}},{"kind":"Field","name":{"kind":"Name","value":"episode"}},{"kind":"Field","name":{"kind":"Name","value":"chapter"}}]}},{"kind":"Field","name":{"kind":"Name","value":"inProgress"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"collections"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CollectionPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"showProgress"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timesSeen"}},{"kind":"Field","name":{"kind":"Name","value":"seasonNumber"}},{"kind":"Field","name":{"kind":"Name","value":"episodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodeNumber"}},{"kind":"Field","name":{"kind":"Name","value":"timesSeen"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastProgress"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodeNumber"}},{"kind":"Field","name":{"kind":"Name","value":"timesSeen"}}]}}]}}]}},{"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":"SeenShowExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenShowExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}},{"kind":"Field","name":{"kind":"Name","value":"season"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenPodcastExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenPodcastExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"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":"SeenPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Seen"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"progress"}},{"kind":"Field","name":{"kind":"Name","value":"reviewId"}},{"kind":"Field","name":{"kind":"Name","value":"startedOn"}},{"kind":"Field","name":{"kind":"Name","value":"finishedOn"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedOn"}},{"kind":"Field","name":{"kind":"Name","value":"manualTimeSpent"}},{"kind":"Field","name":{"kind":"Name","value":"numTimesUpdated"}},{"kind":"Field","name":{"kind":"Name","value":"providerWatchedOn"}},{"kind":"Field","name":{"kind":"Name","value":"showExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenShowExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenPodcastExtraInformationPart"}}]}},{"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":"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"}}]}}]} as unknown as DocumentNode<UserMetadataDetailsQuery, UserMetadataDetailsQueryVariables>;
 export const GetOidcRedirectUrlDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOidcRedirectUrl"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getOidcRedirectUrl"}}]}}]} as unknown as DocumentNode<GetOidcRedirectUrlQuery, GetOidcRedirectUrlQueryVariables>;
 export const UserByOidcIssuerIdDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserByOidcIssuerId"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"oidcIssuerId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userByOidcIssuerId"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"oidcIssuerId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"oidcIssuerId"}}}]}]}}]} as unknown as DocumentNode<UserByOidcIssuerIdQuery, UserByOidcIssuerIdQueryVariables>;
 export const GetOidcTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOidcToken"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"code"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getOidcToken"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"code"},"value":{"kind":"Variable","name":{"kind":"Name","value":"code"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]}}]} as unknown as DocumentNode<GetOidcTokenQuery, GetOidcTokenQueryVariables>;
@@ -3522,22 +3582,22 @@ export const MetadataPartialDetailsDocument = {"kind":"Document","definitions":[
 export const MetadataGroupsListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MetadataGroupsList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MetadataGroupsListInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"metadataGroupsList"},"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<MetadataGroupsListQuery, MetadataGroupsListQueryVariables>;
 export const PeopleListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PeopleList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PeopleListInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"peopleList"},"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<PeopleListQuery, PeopleListQueryVariables>;
 export const UserAccessLinksDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserAccessLinks"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userAccessLinks"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"isDemo"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}},{"kind":"Field","name":{"kind":"Name","value":"expiresOn"}},{"kind":"Field","name":{"kind":"Name","value":"timesUsed"}},{"kind":"Field","name":{"kind":"Name","value":"isRevoked"}},{"kind":"Field","name":{"kind":"Name","value":"maximumUses"}},{"kind":"Field","name":{"kind":"Name","value":"isAccountDefault"}},{"kind":"Field","name":{"kind":"Name","value":"isMutationAllowed"}}]}}]}}]} as unknown as DocumentNode<UserAccessLinksQuery, UserAccessLinksQueryVariables>;
-export const DailyUserActivitiesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"DailyUserActivities"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DailyUserActivitiesInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dailyUserActivities"},"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":"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":"totalReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"workoutCount"}},{"kind":"Field","name":{"kind":"Name","value":"measurementCount"}},{"kind":"Field","name":{"kind":"Name","value":"audioBookCount"}},{"kind":"Field","name":{"kind":"Name","value":"animeCount"}},{"kind":"Field","name":{"kind":"Name","value":"bookCount"}},{"kind":"Field","name":{"kind":"Name","value":"podcastCount"}},{"kind":"Field","name":{"kind":"Name","value":"mangaCount"}},{"kind":"Field","name":{"kind":"Name","value":"showCount"}},{"kind":"Field","name":{"kind":"Name","value":"movieCount"}},{"kind":"Field","name":{"kind":"Name","value":"videoGameCount"}},{"kind":"Field","name":{"kind":"Name","value":"visualNovelCount"}}]}}]}}]}}]} as unknown as DocumentNode<DailyUserActivitiesQuery, DailyUserActivitiesQueryVariables>;
-export const ExerciseDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ExerciseDetails"},"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":"exerciseDetails"},"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":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"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":"muscles"}},{"kind":"Field","name":{"kind":"Name","value":"createdByUserId"}},{"kind":"Field","name":{"kind":"Name","value":"attributes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"instructions"}},{"kind":"Field","name":{"kind":"Name","value":"images"}}]}}]}}]}}]} as unknown as DocumentNode<ExerciseDetailsQuery, ExerciseDetailsQueryVariables>;
-export const ExercisesListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ExercisesList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ExercisesListInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"exercisesList"},"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":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"muscle"}},{"kind":"Field","name":{"kind":"Name","value":"numTimesInteracted"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedOn"}}]}}]}}]}}]} as unknown as DocumentNode<ExercisesListQuery, ExercisesListQueryVariables>;
+export const ExerciseDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ExerciseDetails"},"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":"exerciseDetails"},"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":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"level"}},{"kind":"Field","name":{"kind":"Name","value":"force"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"muscles"}},{"kind":"Field","name":{"kind":"Name","value":"mechanic"}},{"kind":"Field","name":{"kind":"Name","value":"equipment"}},{"kind":"Field","name":{"kind":"Name","value":"createdByUserId"}},{"kind":"Field","name":{"kind":"Name","value":"attributes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"instructions"}},{"kind":"Field","name":{"kind":"Name","value":"images"}}]}}]}}]}}]} as unknown as DocumentNode<ExerciseDetailsQuery, ExerciseDetailsQueryVariables>;
+export const ExercisesListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ExercisesList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ExercisesListInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"exercisesList"},"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":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"muscle"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedOn"}},{"kind":"Field","name":{"kind":"Name","value":"numTimesInteracted"}}]}}]}}]}}]} as unknown as DocumentNode<ExercisesListQuery, ExercisesListQueryVariables>;
 export const ImportReportsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ImportReports"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"importReports"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"progress"}},{"kind":"Field","name":{"kind":"Name","value":"startedOn"}},{"kind":"Field","name":{"kind":"Name","value":"finishedOn"}},{"kind":"Field","name":{"kind":"Name","value":"wasSuccess"}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"import"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}}]}},{"kind":"Field","name":{"kind":"Name","value":"failedItems"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"step"}},{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"identifier"}}]}}]}}]}}]}}]} as unknown as DocumentNode<ImportReportsQuery, ImportReportsQueryVariables>;
 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":"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":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"numItems"}}]}}]}}]}}]} 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":"isPro"}},{"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":"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":"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 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":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"displayImages"}},{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"parts"}},{"kind":"Field","name":{"kind":"Name","value":"isPartial"}}]}}]}}]}}]} 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>;
 export const MetadataSearchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MetadataSearch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MetadataSearchInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"metadataSearch"},"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":"databaseId"}},{"kind":"Field","name":{"kind":"Name","value":"hasInteracted"}},{"kind":"Field","name":{"kind":"Name","value":"item"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}}]}}]}}]}}]}}]} as unknown as DocumentNode<MetadataSearchQuery, MetadataSearchQueryVariables>;
 export const PeopleSearchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PeopleSearch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PeopleSearchInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"peopleSearch"},"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":"birthYear"}}]}}]}}]}}]} as unknown as DocumentNode<PeopleSearchQuery, PeopleSearchQueryVariables>;
-export const UserMetadataGroupDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserMetadataGroupDetails"},"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":"userMetadataGroupDetails"},"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":"reviews"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ReviewItemPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"collections"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CollectionPart"}}]}}]}}]}},{"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"}}]}}]}},{"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"}}]}}]} as unknown as DocumentNode<UserMetadataGroupDetailsQuery, UserMetadataGroupDetailsQueryVariables>;
-export const UserPersonDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserPersonDetails"},"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":"userPersonDetails"},"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":"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":"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":"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"}}]}}]}}]} as unknown as DocumentNode<UserPersonDetailsQuery, UserPersonDetailsQueryVariables>;
-export const UserWorkoutDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserWorkoutDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workoutId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userWorkoutDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workoutId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workoutId"}}}],"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":"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":"endTime"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"startTime"}},{"kind":"Field","name":{"kind":"Name","value":"templateId"}},{"kind":"Field","name":{"kind":"Name","value":"repeatedFrom"}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSummaryPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"information"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutInformationPart"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutOrExerciseTotals"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"personalBestsAchieved"}}]}},{"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":"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":"EntityAssetsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EntityAssets"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"images"}},{"kind":"Field","name":{"kind":"Name","value":"videos"}}]}},{"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":"WorkoutSummaryPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSummary"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"numSets"}},{"kind":"Field","name":{"kind":"Name","value":"bestSet"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetRecordPart"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"focused"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lots"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"levels"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"level"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"forces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"force"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"muscles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"muscle"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"equipments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"equipment"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EntityAssetsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"supersets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"notes"}},{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EntityAssetsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"note"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"confirmedAt"}},{"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"}}]}}]}}]}}]}}]} as unknown as DocumentNode<UserWorkoutDetailsQuery, UserWorkoutDetailsQueryVariables>;
-export const UserWorkoutsListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserWorkoutsList"},"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":"userWorkoutsList"},"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":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"endTime"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"startTime"}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSummaryPart"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutOrExerciseTotals"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"personalBestsAchieved"}}]}},{"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":"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":"WorkoutSummaryPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSummary"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"numSets"}},{"kind":"Field","name":{"kind":"Name","value":"bestSet"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetRecordPart"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"focused"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lots"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"levels"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"level"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"forces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"force"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"muscles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"muscle"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"equipments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"equipment"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}}]}}]}}]} as unknown as DocumentNode<UserWorkoutsListQuery, UserWorkoutsListQueryVariables>;
-export const UserWorkoutTemplateDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserWorkoutTemplateDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workoutTemplateId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userWorkoutTemplateDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workoutTemplateId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workoutTemplateId"}}}],"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":"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":"createdOn"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSummaryPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"information"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutInformationPart"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutOrExerciseTotals"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"personalBestsAchieved"}}]}},{"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":"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":"EntityAssetsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EntityAssets"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"images"}},{"kind":"Field","name":{"kind":"Name","value":"videos"}}]}},{"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":"WorkoutSummaryPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSummary"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"numSets"}},{"kind":"Field","name":{"kind":"Name","value":"bestSet"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetRecordPart"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"focused"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lots"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"levels"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"level"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"forces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"force"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"muscles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"muscle"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"equipments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"equipment"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EntityAssetsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"supersets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"notes"}},{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EntityAssetsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"note"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"confirmedAt"}},{"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"}}]}}]}}]}}]}}]} as unknown as DocumentNode<UserWorkoutTemplateDetailsQuery, UserWorkoutTemplateDetailsQueryVariables>;
-export const UserWorkoutTemplatesListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserWorkoutTemplatesList"},"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":"userWorkoutTemplatesList"},"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":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSummaryPart"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutOrExerciseTotals"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"personalBestsAchieved"}}]}},{"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":"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":"WorkoutSummaryPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSummary"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"numSets"}},{"kind":"Field","name":{"kind":"Name","value":"bestSet"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetRecordPart"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"focused"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lots"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"levels"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"level"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"forces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"force"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"muscles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"muscle"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"equipments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"equipment"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}}]}}]}}]} as unknown as DocumentNode<UserWorkoutTemplatesListQuery, UserWorkoutTemplatesListQueryVariables>;
\ No newline at end of file
+export const UserMetadataGroupDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserMetadataGroupDetails"},"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":"userMetadataGroupDetails"},"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":"recentlyConsumed"}},{"kind":"Field","name":{"kind":"Name","value":"reviews"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ReviewItemPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"collections"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CollectionPart"}}]}}]}}]}},{"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"}}]}}]}},{"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"}}]}}]} as unknown as DocumentNode<UserMetadataGroupDetailsQuery, UserMetadataGroupDetailsQueryVariables>;
+export const UserPersonDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserPersonDetails"},"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":"userPersonDetails"},"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":"recentlyConsumed"}},{"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":"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":"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"}}]}}]}}]} as unknown as DocumentNode<UserPersonDetailsQuery, UserPersonDetailsQueryVariables>;
+export const UserWorkoutDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserWorkoutDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workoutId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userWorkoutDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workoutId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workoutId"}}}],"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":"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":"endTime"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"startTime"}},{"kind":"Field","name":{"kind":"Name","value":"templateId"}},{"kind":"Field","name":{"kind":"Name","value":"repeatedFrom"}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSummaryPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"information"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutInformationPart"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutOrExerciseTotals"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"personalBestsAchieved"}}]}},{"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":"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":"EntityAssetsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EntityAssets"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"images"}},{"kind":"Field","name":{"kind":"Name","value":"videos"}}]}},{"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":"WorkoutSummaryPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSummary"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"numSets"}},{"kind":"Field","name":{"kind":"Name","value":"bestSet"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetRecordPart"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"focused"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lots"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"levels"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"level"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"forces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"force"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"muscles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"muscle"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"equipments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"equipment"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EntityAssetsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"supersets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"notes"}},{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EntityAssetsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"note"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"confirmedAt"}},{"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"}}]}}]}}]}}]}}]} as unknown as DocumentNode<UserWorkoutDetailsQuery, UserWorkoutDetailsQueryVariables>;
+export const UserWorkoutsListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserWorkoutsList"},"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":"userWorkoutsList"},"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<UserWorkoutsListQuery, UserWorkoutsListQueryVariables>;
+export const UserWorkoutTemplateDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserWorkoutTemplateDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workoutTemplateId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userWorkoutTemplateDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workoutTemplateId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workoutTemplateId"}}}],"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":"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":"createdOn"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSummaryPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"information"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutInformationPart"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutOrExerciseTotals"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"personalBestsAchieved"}}]}},{"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":"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":"EntityAssetsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EntityAssets"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"images"}},{"kind":"Field","name":{"kind":"Name","value":"videos"}}]}},{"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":"WorkoutSummaryPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSummary"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"numSets"}},{"kind":"Field","name":{"kind":"Name","value":"bestSet"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetRecordPart"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"focused"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lots"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"levels"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"level"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"forces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"force"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"muscles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"muscle"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"equipments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"equipment"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EntityAssetsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"supersets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"exercises"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"notes"}},{"kind":"Field","name":{"kind":"Name","value":"total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutOrExerciseTotalsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"EntityAssetsPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"note"}},{"kind":"Field","name":{"kind":"Name","value":"restTime"}},{"kind":"Field","name":{"kind":"Name","value":"confirmedAt"}},{"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"}}]}}]}}]}}]}}]} as unknown as DocumentNode<UserWorkoutTemplateDetailsQuery, UserWorkoutTemplateDetailsQueryVariables>;
+export const UserWorkoutTemplatesListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserWorkoutTemplatesList"},"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":"userWorkoutTemplatesList"},"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<UserWorkoutTemplatesListQuery, UserWorkoutTemplatesListQueryVariables>;
+export const UserAnalyticsParametersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserAnalyticsParameters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userAnalyticsParameters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"endDate"}},{"kind":"Field","name":{"kind":"Name","value":"startDate"}}]}}]}}]} as unknown as DocumentNode<UserAnalyticsParametersQuery, UserAnalyticsParametersQueryVariables>;
\ No newline at end of file
diff --git a/libs/generated/src/graphql/backend/types.generated.ts b/libs/generated/src/graphql/backend/types.generated.ts
index 0e241c8917..791e83963d 100644
--- a/libs/generated/src/graphql/backend/types.generated.ts
+++ b/libs/generated/src/graphql/backend/types.generated.ts
@@ -57,6 +57,19 @@ export type AnimeSpecificsInput = {
   episodes?: InputMaybe<Scalars['Int']['input']>;
 };
 
+/** The start date must be before the end date. */
+export type ApplicationDateRange = {
+  __typename?: 'ApplicationDateRange';
+  endDate?: Maybe<Scalars['NaiveDate']['output']>;
+  startDate?: Maybe<Scalars['NaiveDate']['output']>;
+};
+
+/** The start date must be before the end date. */
+export type ApplicationDateRangeInput = {
+  endDate?: InputMaybe<Scalars['NaiveDate']['input']>;
+  startDate?: InputMaybe<Scalars['NaiveDate']['input']>;
+};
+
 export type AudioBookSpecifics = {
   __typename?: 'AudioBookSpecifics';
   runtime?: Maybe<Scalars['Int']['output']>;
@@ -83,7 +96,7 @@ export enum BackgroundJob {
   CalculateUserActivitiesAndSummary = 'CALCULATE_USER_ACTIVITIES_AND_SUMMARY',
   PerformBackgroundTasks = 'PERFORM_BACKGROUND_TASKS',
   RecalculateCalendarEvents = 'RECALCULATE_CALENDAR_EVENTS',
-  ReEvaluateUserWorkouts = 'RE_EVALUATE_USER_WORKOUTS',
+  ReviseUserWorkouts = 'REVISE_USER_WORKOUTS',
   SyncIntegrationsData = 'SYNC_INTEGRATIONS_DATA',
   UpdateAllExercises = 'UPDATE_ALL_EXERCISES',
   UpdateAllMetadata = 'UPDATE_ALL_METADATA'
@@ -208,7 +221,7 @@ export type CoreDetails = {
   exerciseParameters: ExerciseParameters;
   fileStorageEnabled: Scalars['Boolean']['output'];
   frontend: FrontendConfig;
-  isPro: Scalars['Boolean']['output'];
+  isServerKeyValidated: Scalars['Boolean']['output'];
   localAuthDisabled: Scalars['Boolean']['output'];
   metadataLotSourceMappings: Array<MetadataLotSourceMappings>;
   metadataProviderLanguages: Array<ProviderLanguageInformation>;
@@ -305,12 +318,6 @@ export type CreateUserNotificationPlatformInput = {
   priority?: InputMaybe<Scalars['Int']['input']>;
 };
 
-export type DailyUserActivitiesInput = {
-  endDate?: InputMaybe<Scalars['NaiveDate']['input']>;
-  groupBy?: InputMaybe<DailyUserActivitiesResponseGroupedBy>;
-  startDate?: InputMaybe<Scalars['NaiveDate']['input']>;
-};
-
 export type DailyUserActivitiesResponse = {
   __typename?: 'DailyUserActivitiesResponse';
   groupedBy: DailyUserActivitiesResponseGroupedBy;
@@ -327,6 +334,19 @@ export enum DailyUserActivitiesResponseGroupedBy {
   Year = 'YEAR'
 }
 
+export type DailyUserActivityHourRecord = {
+  __typename?: 'DailyUserActivityHourRecord';
+  entities: Array<DailyUserActivityHourRecordEntity>;
+  hour: Scalars['Int']['output'];
+};
+
+export type DailyUserActivityHourRecordEntity = {
+  __typename?: 'DailyUserActivityHourRecordEntity';
+  entityId: Scalars['String']['output'];
+  entityLot: EntityLot;
+  metadataLot?: Maybe<MediaLot>;
+};
+
 export type DailyUserActivityItem = {
   __typename?: 'DailyUserActivityItem';
   animeCount: Scalars['Int']['output'];
@@ -334,7 +354,6 @@ export type DailyUserActivityItem = {
   bookCount: Scalars['Int']['output'];
   day: Scalars['NaiveDate']['output'];
   mangaCount: Scalars['Int']['output'];
-  measurementCount: Scalars['Int']['output'];
   movieCount: Scalars['Int']['output'];
   podcastCount: Scalars['Int']['output'];
   showCount: Scalars['Int']['output'];
@@ -359,13 +378,13 @@ export type DailyUserActivityItem = {
   totalWorkoutReps: Scalars['Int']['output'];
   totalWorkoutRestTime: Scalars['Int']['output'];
   totalWorkoutWeight: Scalars['Int']['output'];
+  userMeasurementCount: Scalars['Int']['output'];
   videoGameCount: Scalars['Int']['output'];
   visualNovelCount: Scalars['Int']['output'];
   workoutCount: Scalars['Int']['output'];
 };
 
 export enum DashboardElementLot {
-  Activity = 'ACTIVITY',
   InProgress = 'IN_PROGRESS',
   Recommendations = 'RECOMMENDATIONS',
   Summary = 'SUMMARY',
@@ -477,6 +496,7 @@ export type Exercise = {
   lot: ExerciseLot;
   mechanic?: Maybe<ExerciseMechanic>;
   muscles: Array<ExerciseMuscle>;
+  name: Scalars['String']['output'];
   source: ExerciseSource;
 };
 
@@ -533,11 +553,11 @@ export type ExerciseInput = {
   attributes: ExerciseAttributesInput;
   equipment?: InputMaybe<ExerciseEquipment>;
   force?: InputMaybe<ExerciseForce>;
-  id: Scalars['String']['input'];
   level: ExerciseLevel;
   lot: ExerciseLot;
   mechanic?: InputMaybe<ExerciseMechanic>;
   muscles: Array<ExerciseMuscle>;
+  name: Scalars['String']['input'];
 };
 
 export enum ExerciseLevel {
@@ -563,6 +583,7 @@ export type ExerciseListItem = {
   lastUpdatedOn?: Maybe<Scalars['DateTime']['output']>;
   lot: ExerciseLot;
   muscle?: Maybe<ExerciseMuscle>;
+  name: Scalars['String']['output'];
   numTimesInteracted?: Maybe<Scalars['Int']['output']>;
 };
 
@@ -650,6 +671,24 @@ export type ExternalIdentifiers = {
   tvdbId?: Maybe<Scalars['Int']['output']>;
 };
 
+export type FitnessAnalyticsEquipment = {
+  __typename?: 'FitnessAnalyticsEquipment';
+  count: Scalars['Int']['output'];
+  equipment: ExerciseEquipment;
+};
+
+export type FitnessAnalyticsExercise = {
+  __typename?: 'FitnessAnalyticsExercise';
+  count: Scalars['Int']['output'];
+  exercise: Scalars['String']['output'];
+};
+
+export type FitnessAnalyticsMuscle = {
+  __typename?: 'FitnessAnalyticsMuscle';
+  count: Scalars['Int']['output'];
+  muscle: ExerciseMuscle;
+};
+
 export type FrontendConfig = {
   __typename?: 'FrontendConfig';
   /** A message to be displayed on the dashboard. */
@@ -893,7 +932,8 @@ export enum IntegrationProvider {
   JellyfinSink = 'JELLYFIN_SINK',
   Kodi = 'KODI',
   Komga = 'KOMGA',
-  Plex = 'PLEX',
+  PlexSink = 'PLEX_SINK',
+  PlexYank = 'PLEX_YANK',
   Radarr = 'RADARR',
   Sonarr = 'SONARR'
 }
@@ -908,7 +948,9 @@ export type IntegrationSourceSpecificsInput = {
   komgaPassword?: InputMaybe<Scalars['String']['input']>;
   komgaProvider?: InputMaybe<MediaSource>;
   komgaUsername?: InputMaybe<Scalars['String']['input']>;
-  plexUsername?: InputMaybe<Scalars['String']['input']>;
+  plexSinkUsername?: InputMaybe<Scalars['String']['input']>;
+  plexYankBaseUrl?: InputMaybe<Scalars['String']['input']>;
+  plexYankToken?: InputMaybe<Scalars['String']['input']>;
   radarrApiKey?: InputMaybe<Scalars['String']['input']>;
   radarrBaseUrl?: InputMaybe<Scalars['String']['input']>;
   radarrProfileId?: InputMaybe<Scalars['Int']['input']>;
@@ -1717,8 +1759,8 @@ export type ProcessAccessLinkResult = ProcessAccessLinkError | ProcessAccessLink
 export type ProcessedExercise = {
   __typename?: 'ProcessedExercise';
   assets?: Maybe<EntityAssets>;
+  id: Scalars['String']['output'];
   lot: ExerciseLot;
-  name: Scalars['String']['output'];
   notes: Array<Scalars['String']['output']>;
   sets: Array<WorkoutSetRecord>;
   total?: Maybe<WorkoutOrExerciseTotals>;
@@ -1751,8 +1793,6 @@ export type QueryRoot = {
   collectionContents: CollectionContents;
   /** Get some primary information about the service. */
   coreDetails: CoreDetails;
-  /** Get daily user activities for the currently logged in user. */
-  dailyUserActivities: DailyUserActivitiesResponse;
   /** Get details about an exercise. */
   exerciseDetails: Exercise;
   /** Get a paginated list of exercises in the database. */
@@ -1769,8 +1809,6 @@ export type QueryRoot = {
   getPresignedS3Url: Scalars['String']['output'];
   /** Get all the import jobs deployed by the user. */
   importReports: Array<ImportReport>;
-  /** Get a summary of all the media items that have been consumed by this user. */
-  latestUserSummary: DailyUserActivityItem;
   /** Get details about a media present in the database. */
   metadataDetails: GraphqlMetadataDetails;
   /** Get details about a metadata group present in the database. */
@@ -1793,6 +1831,10 @@ export type QueryRoot = {
   personDetails: PersonDetails;
   /** Get all access links generated by the currently logged in user. */
   userAccessLinks: Array<AccessLink>;
+  /** Get the analytics for the currently logged in user. */
+  userAnalytics: UserAnalytics;
+  /** Get the analytics parameters for the currently logged in user. */
+  userAnalyticsParameters: ApplicationDateRange;
   /** Get user by OIDC issuer ID. */
   userByOidcIssuerId?: Maybe<Scalars['String']['output']>;
   /** Get calendar events for a user between a given date range. */
@@ -1826,9 +1868,9 @@ export type QueryRoot = {
   /** Get information about a workout template. */
   userWorkoutTemplateDetails: UserWorkoutTemplateDetails;
   /** Get a paginated list of templates created by the user. */
-  userWorkoutTemplatesList: WorkoutTemplateListResults;
+  userWorkoutTemplatesList: IdResults;
   /** Get a paginated list of workouts done by the user. */
-  userWorkoutsList: WorkoutListResults;
+  userWorkoutsList: IdResults;
   /** Get details about all the users in the service. */
   usersList: Array<User>;
 };
@@ -1839,11 +1881,6 @@ export type QueryRootCollectionContentsArgs = {
 };
 
 
-export type QueryRootDailyUserActivitiesArgs = {
-  input: DailyUserActivitiesInput;
-};
-
-
 export type QueryRootExerciseDetailsArgs = {
   exerciseId: Scalars['String']['input'];
 };
@@ -1924,6 +1961,11 @@ export type QueryRootPersonDetailsArgs = {
 };
 
 
+export type QueryRootUserAnalyticsArgs = {
+  input: UserAnalyticsInput;
+};
+
+
 export type QueryRootUserByOidcIssuerIdArgs = {
   oidcIssuerId: Scalars['String']['input'];
 };
@@ -2201,12 +2243,12 @@ export type UpdateCustomExerciseInput = {
   attributes: ExerciseAttributesInput;
   equipment?: InputMaybe<ExerciseEquipment>;
   force?: InputMaybe<ExerciseForce>;
-  id: Scalars['String']['input'];
   level: ExerciseLevel;
   lot: ExerciseLot;
   mechanic?: InputMaybe<ExerciseMechanic>;
   muscles: Array<ExerciseMuscle>;
-  oldName: Scalars['String']['input'];
+  name: Scalars['String']['input'];
+  oldId: Scalars['String']['input'];
   shouldDelete?: InputMaybe<Scalars['Boolean']['input']>;
 };
 
@@ -2226,7 +2268,6 @@ export type UpdateUserExerciseSettings = {
 
 export type UpdateUserInput = {
   adminAccessToken?: InputMaybe<Scalars['String']['input']>;
-  extraInformation?: InputMaybe<Scalars['JSON']['input']>;
   isDisabled?: InputMaybe<Scalars['Boolean']['input']>;
   lot?: InputMaybe<UserLot>;
   password?: InputMaybe<Scalars['String']['input']>;
@@ -2264,6 +2305,23 @@ export type User = {
   preferences: UserPreferences;
 };
 
+export type UserAnalytics = {
+  __typename?: 'UserAnalytics';
+  activities: DailyUserActivitiesResponse;
+  fitness: UserFitnessAnalytics;
+  hours: Array<DailyUserActivityHourRecord>;
+};
+
+export type UserAnalyticsFeaturesEnabledPreferences = {
+  __typename?: 'UserAnalyticsFeaturesEnabledPreferences';
+  enabled: Scalars['Boolean']['output'];
+};
+
+export type UserAnalyticsInput = {
+  dateRange: ApplicationDateRangeInput;
+  groupBy?: InputMaybe<DailyUserActivitiesResponseGroupedBy>;
+};
+
 export type UserCalendarEventInput = {
   month: Scalars['Int']['input'];
   year: Scalars['Int']['input'];
@@ -2307,11 +2365,27 @@ export type UserExerciseInput = {
 
 export type UserFeaturesEnabledPreferences = {
   __typename?: 'UserFeaturesEnabledPreferences';
+  analytics: UserAnalyticsFeaturesEnabledPreferences;
   fitness: UserFitnessFeaturesEnabledPreferences;
   media: UserMediaFeaturesEnabledPreferences;
   others: UserOthersFeaturesEnabledPreferences;
 };
 
+export type UserFitnessAnalytics = {
+  __typename?: 'UserFitnessAnalytics';
+  measurementCount: Scalars['Int']['output'];
+  workoutCount: Scalars['Int']['output'];
+  workoutDistance: Scalars['Int']['output'];
+  workoutDuration: Scalars['Int']['output'];
+  workoutEquipments: Array<FitnessAnalyticsEquipment>;
+  workoutExercises: Array<FitnessAnalyticsExercise>;
+  workoutMuscles: Array<FitnessAnalyticsMuscle>;
+  workoutPersonalBests: Scalars['Int']['output'];
+  workoutReps: Scalars['Int']['output'];
+  workoutRestTime: Scalars['Int']['output'];
+  workoutWeight: Scalars['Int']['output'];
+};
+
 export type UserFitnessExercisesPreferences = {
   __typename?: 'UserFitnessExercisesPreferences';
   setRestTimers: SetRestTimersSettings;
@@ -2536,6 +2610,8 @@ export type UserMetadataDetails = {
   nextEntry?: Maybe<UserMediaNextEntry>;
   /** The seen progress of this media if it is a podcast. */
   podcastProgress?: Maybe<Array<UserMetadataDetailsEpisodeProgress>>;
+  /** Whether this media has been recently interacted with */
+  recentlyConsumed: Scalars['Boolean']['output'];
   /** The public reviews of this media. */
   reviews: Array<ReviewItem>;
   /** The number of users who have seen this media. */
@@ -2562,6 +2638,7 @@ export type UserMetadataDetailsShowSeasonProgress = {
 export type UserMetadataGroupDetails = {
   __typename?: 'UserMetadataGroupDetails';
   collections: Array<Collection>;
+  recentlyConsumed: Scalars['Boolean']['output'];
   reviews: Array<ReviewItem>;
 };
 
@@ -2580,6 +2657,7 @@ export type UserOthersFeaturesEnabledPreferences = {
 export type UserPersonDetails = {
   __typename?: 'UserPersonDetails';
   collections: Array<Collection>;
+  recentlyConsumed: Scalars['Boolean']['output'];
   reviews: Array<ReviewItem>;
 };
 
@@ -2635,6 +2713,7 @@ export type UserToExerciseHistoryExtraInformation = {
 
 export type UserToExerciseSettingsExtraInformation = {
   __typename?: 'UserToExerciseSettingsExtraInformation';
+  excludeFromAnalytics: Scalars['Boolean']['output'];
   setRestTimers: SetRestTimersSettings;
 };
 
@@ -2775,12 +2854,6 @@ export type WorkoutLevelFocusedSummary = {
   level: ExerciseLevel;
 };
 
-export type WorkoutListResults = {
-  __typename?: 'WorkoutListResults';
-  details: SearchDetails;
-  items: Array<Workout>;
-};
-
 export type WorkoutLotFocusedSummary = {
   __typename?: 'WorkoutLotFocusedSummary';
   exercises: Array<Scalars['Int']['output']>;
@@ -2857,8 +2930,8 @@ export type WorkoutSummary = {
 export type WorkoutSummaryExercise = {
   __typename?: 'WorkoutSummaryExercise';
   bestSet?: Maybe<WorkoutSetRecord>;
+  id: Scalars['String']['output'];
   lot?: Maybe<ExerciseLot>;
-  name: Scalars['String']['output'];
   numSets: Scalars['Int']['output'];
 };
 
@@ -2886,9 +2959,3 @@ export type WorkoutTemplate = {
   summary: WorkoutSummary;
   visibility: Visibility;
 };
-
-export type WorkoutTemplateListResults = {
-  __typename?: 'WorkoutTemplateListResults';
-  details: SearchDetails;
-  items: Array<WorkoutTemplate>;
-};
diff --git a/libs/graphql/src/backend/queries/UserAnalytics.gql b/libs/graphql/src/backend/queries/UserAnalytics.gql
new file mode 100644
index 0000000000..2f998a7e54
--- /dev/null
+++ b/libs/graphql/src/backend/queries/UserAnalytics.gql
@@ -0,0 +1,75 @@
+
+query UserAnalytics($input: UserAnalyticsInput!) {
+	userAnalytics(input: $input) {
+		hours {
+			hour
+			entities {
+				entityId
+				entityLot
+				metadataLot
+			}
+		}
+		activities {
+			groupedBy
+			totalCount
+			totalDuration
+			items {
+				day
+				bookCount
+				showCount
+				totalCount
+				movieCount
+				animeCount
+				mangaCount
+				workoutCount
+				podcastCount
+				totalDuration
+				totalBookPages
+				audioBookCount
+				videoGameCount
+				totalReviewCount
+				visualNovelCount
+				totalWorkoutReps
+				totalShowDuration
+				totalMovieDuration
+				totalMetadataCount
+				totalWorkoutWeight
+				totalPodcastDuration
+				totalWorkoutDistance
+				totalWorkoutRestTime
+				totalWorkoutDuration
+				userMeasurementCount
+				totalVideoGameDuration
+				totalAudioBookDuration
+				totalPersonReviewCount
+				totalVisualNovelDuration
+				totalMetadataReviewCount
+				totalWorkoutPersonalBests
+				totalCollectionReviewCount
+				totalMetadataGroupReviewCount
+			}
+		}
+		fitness {
+			workoutReps
+			workoutCount
+			workoutWeight
+			workoutDistance
+			workoutDuration
+			workoutRestTime
+			measurementCount
+			workoutPersonalBests
+			workoutExercises {
+				count
+				exercise
+			}
+			workoutMuscles {
+				count
+				muscle
+			}
+			workoutEquipments {
+				count
+				equipment
+			}
+		}
+	}
+}
diff --git a/libs/graphql/src/backend/queries/UserDetails.gql b/libs/graphql/src/backend/queries/UserDetails.gql
index c5f576ba36..b879d2f02d 100644
--- a/libs/graphql/src/backend/queries/UserDetails.gql
+++ b/libs/graphql/src/backend/queries/UserDetails.gql
@@ -77,6 +77,9 @@ query UserDetails {
 					enabled
 				}
 				featuresEnabled {
+					analytics {
+						enabled
+					}
 					others {
 						calendar
 						collections
diff --git a/libs/graphql/src/backend/queries/UserExerciseDetails.gql b/libs/graphql/src/backend/queries/UserExerciseDetails.gql
index 2604e85489..1ef9fa24aa 100644
--- a/libs/graphql/src/backend/queries/UserExerciseDetails.gql
+++ b/libs/graphql/src/backend/queries/UserExerciseDetails.gql
@@ -21,6 +21,7 @@ query UserExerciseDetails($exerciseId: String!) {
 			exerciseNumTimesInteracted
 			exerciseExtraInformation {
 				settings {
+					excludeFromAnalytics
 					setRestTimers {
 						...SetRestTimersPart
 					}
@@ -35,9 +36,9 @@ query UserExerciseDetails($exerciseId: String!) {
 				personalBests {
 					lot
 					sets {
+						setIdx
 						workoutId
 						exerciseIdx
-						setIdx
 					}
 				}
 			}
diff --git a/libs/graphql/src/backend/queries/UserMetadataDetails.gql b/libs/graphql/src/backend/queries/UserMetadataDetails.gql
index 9b3bdeb9a6..3b47dfd65b 100644
--- a/libs/graphql/src/backend/queries/UserMetadataDetails.gql
+++ b/libs/graphql/src/backend/queries/UserMetadataDetails.gql
@@ -2,27 +2,28 @@ query UserMetadataDetails($metadataId: String!) {
 	userMetadataDetails(metadataId: $metadataId) {
 		mediaReason
 		hasInteracted
-		collections {
-			...CollectionPart
-		}
-		inProgress {
-			...SeenPart
-		}
-		history {
-			...SeenPart
-		}
 		averageRating
+		seenByAllCount
+		seenByUserCount
+		recentlyConsumed
 		reviews {
 			...ReviewItemPart
 		}
-		seenByAllCount
-		seenByUserCount
+		history {
+			...SeenPart
+		}
 		nextEntry {
 			season
 			volume
 			episode
 			chapter
 		}
+		inProgress {
+			...SeenPart
+		}
+		collections {
+			...CollectionPart
+		}
 		showProgress {
 			timesSeen
 			seasonNumber
diff --git a/libs/graphql/src/backend/queries/combined.gql b/libs/graphql/src/backend/queries/combined.gql
index d4a988d9f1..66b6f85754 100644
--- a/libs/graphql/src/backend/queries/combined.gql
+++ b/libs/graphql/src/backend/queries/combined.gql
@@ -148,39 +148,17 @@ query UserAccessLinks {
 	}
 }
 
-query DailyUserActivities($input: DailyUserActivitiesInput!) {
-	dailyUserActivities(input: $input) {
-		groupedBy
-		totalCount
-		totalDuration
-		items {
-			day
-			totalReviewCount
-			workoutCount
-			measurementCount
-			audioBookCount
-			animeCount
-			bookCount
-			podcastCount
-			mangaCount
-			showCount
-			movieCount
-			videoGameCount
-			visualNovelCount
-		}
-	}
-}
-
 query ExerciseDetails($exerciseId: String!) {
 	exerciseDetails(exerciseId: $exerciseId) {
 		id
 		lot
-		source
+		name
 		level
 		force
+		source
+		muscles
 		mechanic
 		equipment
-		muscles
 		createdByUserId
 		attributes {
 			instructions
@@ -198,10 +176,11 @@ query ExercisesList($input: ExercisesListInput!) {
 		items {
 			id
 			lot
+			name
 			image
 			muscle
-			numTimesInteracted
 			lastUpdatedOn
+			numTimesInteracted
 		}
 	}
 }
@@ -288,7 +267,6 @@ query CollectionContents($input: CollectionContentsInput!) {
 
 query CoreDetails {
 	coreDetails {
-		isPro
 		version
 		docsLink
 		pageSize
@@ -301,6 +279,7 @@ query CoreDetails {
 		tokenValidForDays
 		localAuthDisabled
 		fileStorageEnabled
+		isServerKeyValidated
 		metadataLotSourceMappings {
 			lot
 			sources
@@ -416,6 +395,7 @@ query PeopleSearch($input: PeopleSearchInput!) {
 
 query UserMetadataGroupDetails($metadataGroupId: String!) {
 	userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {
+		recentlyConsumed
 		reviews {
 			...ReviewItemPart
 		}
@@ -427,6 +407,7 @@ query UserMetadataGroupDetails($metadataGroupId: String!) {
 
 query UserPersonDetails($personId: String!) {
 	userPersonDetails(personId: $personId) {
+		recentlyConsumed
 		collections {
 			...CollectionPart
 		}
@@ -465,16 +446,7 @@ query UserWorkoutsList($input: SearchInput!) {
 			total
 			nextPage
 		}
-		items {
-			id
-			name
-			endTime
-			duration
-			startTime
-			summary {
-				...WorkoutSummaryPart
-			}
-		}
+		items
 	}
 }
 
@@ -504,14 +476,13 @@ query UserWorkoutTemplatesList($input: SearchInput!) {
 			total
 			nextPage
 		}
-		items {
-			id
-			name
-			createdOn
-			visibility
-			summary {
-				...WorkoutSummaryPart
-			}
-		}
+		items
+	}
+}
+
+query UserAnalyticsParameters {
+	userAnalyticsParameters {
+		endDate
+		startDate
 	}
 }
diff --git a/libs/graphql/src/backend/queries/fragments.gql b/libs/graphql/src/backend/queries/fragments.gql
index 9c7536c659..c192b5df2e 100644
--- a/libs/graphql/src/backend/queries/fragments.gql
+++ b/libs/graphql/src/backend/queries/fragments.gql
@@ -104,8 +104,8 @@ fragment WorkoutSummaryPart on WorkoutSummary {
 		...WorkoutOrExerciseTotalsPart
 	}
 	exercises {
+		id
 		lot
-		name
 		numSets
 		bestSet {
 			...WorkoutSetRecordPart
@@ -189,8 +189,8 @@ fragment WorkoutInformationPart on WorkoutInformation {
 		exercises
 	}
 	exercises {
+		id
 		lot
-		name
 		notes
 		total {
 			...WorkoutOrExerciseTotalsPart
diff --git a/libs/transactional/package.json b/libs/transactional/package.json
index 7d52e3367c..194b9b21d5 100644
--- a/libs/transactional/package.json
+++ b/libs/transactional/package.json
@@ -2,11 +2,11 @@
 	"name": "@ryot/transactional",
 	"private": true,
 	"dependencies": {
-		"@react-email/components": "0.0.28",
+		"@react-email/components": "0.0.29",
 		"@ryot/ts-utils": "workspace:*",
 		"react": "18.3.1",
 		"react-dom": "18.3.1",
-		"react-email": "3.0.2"
+		"react-email": "3.0.3"
 	},
 	"devDependencies": {
 		"@types/react": "18.3.12",
diff --git a/libs/ts-utils/src/index.ts b/libs/ts-utils/src/index.ts
index 1109bb4406..47d714882d 100644
--- a/libs/ts-utils/src/index.ts
+++ b/libs/ts-utils/src/index.ts
@@ -43,6 +43,11 @@ export const humanizeDuration = (
 	return humanizer.humanize(duration, options);
 };
 
+export const formatQuantityWithCompactNotation = (value: number) =>
+	new Intl.NumberFormat("en-US", {
+		notation: "compact",
+	}).format(value);
+
 /**
  * Format a `Date` into a Rust `NaiveDate`
  */
diff --git a/yarn.lock b/yarn.lock
index 7bd939f47c..873a5c948b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3891,72 +3891,72 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@next/env@npm:14.2.10":
-  version: 14.2.10
-  resolution: "@next/env@npm:14.2.10"
-  checksum: 10/52754033a280e2ea804bcf15df5f13fe24db6d7b9b571676e8ba79735675d66716c2d9e51a2f5b2ba6c6492178f4c82c572b3f4097efd890dfda38fd8a4328c4
+"@next/env@npm:14.2.16":
+  version: 14.2.16
+  resolution: "@next/env@npm:14.2.16"
+  checksum: 10/b544711fafc04bcc5c6334885e6683398ee4ab3e2de5bfa3e34fca402f2882a1ca9b8a45d307204a218206b531e18de46b6705ed1f62e774f09fc214a94905cd
   languageName: node
   linkType: hard
 
-"@next/swc-darwin-arm64@npm:14.2.10":
-  version: 14.2.10
-  resolution: "@next/swc-darwin-arm64@npm:14.2.10"
+"@next/swc-darwin-arm64@npm:14.2.16":
+  version: 14.2.16
+  resolution: "@next/swc-darwin-arm64@npm:14.2.16"
   conditions: os=darwin & cpu=arm64
   languageName: node
   linkType: hard
 
-"@next/swc-darwin-x64@npm:14.2.10":
-  version: 14.2.10
-  resolution: "@next/swc-darwin-x64@npm:14.2.10"
+"@next/swc-darwin-x64@npm:14.2.16":
+  version: 14.2.16
+  resolution: "@next/swc-darwin-x64@npm:14.2.16"
   conditions: os=darwin & cpu=x64
   languageName: node
   linkType: hard
 
-"@next/swc-linux-arm64-gnu@npm:14.2.10":
-  version: 14.2.10
-  resolution: "@next/swc-linux-arm64-gnu@npm:14.2.10"
+"@next/swc-linux-arm64-gnu@npm:14.2.16":
+  version: 14.2.16
+  resolution: "@next/swc-linux-arm64-gnu@npm:14.2.16"
   conditions: os=linux & cpu=arm64 & libc=glibc
   languageName: node
   linkType: hard
 
-"@next/swc-linux-arm64-musl@npm:14.2.10":
-  version: 14.2.10
-  resolution: "@next/swc-linux-arm64-musl@npm:14.2.10"
+"@next/swc-linux-arm64-musl@npm:14.2.16":
+  version: 14.2.16
+  resolution: "@next/swc-linux-arm64-musl@npm:14.2.16"
   conditions: os=linux & cpu=arm64 & libc=musl
   languageName: node
   linkType: hard
 
-"@next/swc-linux-x64-gnu@npm:14.2.10":
-  version: 14.2.10
-  resolution: "@next/swc-linux-x64-gnu@npm:14.2.10"
+"@next/swc-linux-x64-gnu@npm:14.2.16":
+  version: 14.2.16
+  resolution: "@next/swc-linux-x64-gnu@npm:14.2.16"
   conditions: os=linux & cpu=x64 & libc=glibc
   languageName: node
   linkType: hard
 
-"@next/swc-linux-x64-musl@npm:14.2.10":
-  version: 14.2.10
-  resolution: "@next/swc-linux-x64-musl@npm:14.2.10"
+"@next/swc-linux-x64-musl@npm:14.2.16":
+  version: 14.2.16
+  resolution: "@next/swc-linux-x64-musl@npm:14.2.16"
   conditions: os=linux & cpu=x64 & libc=musl
   languageName: node
   linkType: hard
 
-"@next/swc-win32-arm64-msvc@npm:14.2.10":
-  version: 14.2.10
-  resolution: "@next/swc-win32-arm64-msvc@npm:14.2.10"
+"@next/swc-win32-arm64-msvc@npm:14.2.16":
+  version: 14.2.16
+  resolution: "@next/swc-win32-arm64-msvc@npm:14.2.16"
   conditions: os=win32 & cpu=arm64
   languageName: node
   linkType: hard
 
-"@next/swc-win32-ia32-msvc@npm:14.2.10":
-  version: 14.2.10
-  resolution: "@next/swc-win32-ia32-msvc@npm:14.2.10"
+"@next/swc-win32-ia32-msvc@npm:14.2.16":
+  version: 14.2.16
+  resolution: "@next/swc-win32-ia32-msvc@npm:14.2.16"
   conditions: os=win32 & cpu=ia32
   languageName: node
   linkType: hard
 
-"@next/swc-win32-x64-msvc@npm:14.2.10":
-  version: 14.2.10
-  resolution: "@next/swc-win32-x64-msvc@npm:14.2.10"
+"@next/swc-win32-x64-msvc@npm:14.2.16":
+  version: 14.2.16
+  resolution: "@next/swc-win32-x64-msvc@npm:14.2.16"
   conditions: os=win32 & cpu=x64
   languageName: node
   linkType: hard
@@ -5005,222 +5005,222 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@react-email/body@npm:0.0.10":
-  version: 0.0.10
-  resolution: "@react-email/body@npm:0.0.10"
+"@react-email/body@npm:0.0.11":
+  version: 0.0.11
+  resolution: "@react-email/body@npm:0.0.11"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/68c1cd8e5f53476849981c625cabc172eca02b53dc97a883e5d7b21264d4b53e005e085abbec7b1635ad575b570becfa6e6a96adeb9e75a086c23243bc0d7080
+  checksum: 10/bd1abb325b383419ce651c837d5c228862ff181ec2e74357249613ede02295faf1d05fddcc1fde1630c2c99bbc929a874db6a01fb1c70f768e41fab6f610184c
   languageName: node
   linkType: hard
 
-"@react-email/button@npm:0.0.18":
-  version: 0.0.18
-  resolution: "@react-email/button@npm:0.0.18"
+"@react-email/button@npm:0.0.19":
+  version: 0.0.19
+  resolution: "@react-email/button@npm:0.0.19"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/1f4c37a7d337d6b234467ff097c2d86df703816f669b4ba135a5892095659b8c40d995b34fa9aa357eedc8578daf2509b5bbcfa4b594b05c9faa50c9d26c9eba
+  checksum: 10/d5385be40eec09e38af8910f12cfb531940de1603731ad8711a857dd36dcd41b927addb466b6978b01d49c668c91986ec007b17c1d0fef938170b19b00c0fbe5
   languageName: node
   linkType: hard
 
-"@react-email/code-block@npm:0.0.10":
-  version: 0.0.10
-  resolution: "@react-email/code-block@npm:0.0.10"
+"@react-email/code-block@npm:0.0.11":
+  version: 0.0.11
+  resolution: "@react-email/code-block@npm:0.0.11"
   dependencies:
     prismjs: "npm:1.29.0"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/6bb85b201392a3da06ad1db5ba360e28232dcb014680d4817d8e240409da25f8d0252541c73de7f128963d785e69f0de6f8571e31f7f42a567fb3f25943b4a96
+  checksum: 10/380fcb2da86c897bc4530bcdbe30598f43c175da2f417dc1ca4596cc201acd88bda16093dbb09207e9949d1d172775b4585f69145e5e4ab4d0ea845a88256a49
   languageName: node
   linkType: hard
 
-"@react-email/code-inline@npm:0.0.4":
-  version: 0.0.4
-  resolution: "@react-email/code-inline@npm:0.0.4"
+"@react-email/code-inline@npm:0.0.5":
+  version: 0.0.5
+  resolution: "@react-email/code-inline@npm:0.0.5"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/6d379ce85f76e27074b3494ed21ba32047583bf4f9a68c7256c95c7b1b4a67fd69ffc7f94a923a7149e5378b17164bb1cc2ccec82fe0a309f2c62a0a2fd90cb7
+  checksum: 10/ecca1173b3f019d19c16cc0af7202bcd4eef09054e6ef423482f97918d136cb234ed21067b0690df279e8addd73b29d0ea3d7dadef4d7b68c256783a114853f8
   languageName: node
   linkType: hard
 
-"@react-email/column@npm:0.0.12":
-  version: 0.0.12
-  resolution: "@react-email/column@npm:0.0.12"
+"@react-email/column@npm:0.0.13":
+  version: 0.0.13
+  resolution: "@react-email/column@npm:0.0.13"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/d6b70e3c7a3ebc405945572a3d3dae8ec9e5c4ab1c70ab58c00c8467a1dcce29c44a513f550f5c70d0d960111d7000c487d75751e6f1f27b6c9c2c08245b787c
-  languageName: node
-  linkType: hard
-
-"@react-email/components@npm:0.0.28":
-  version: 0.0.28
-  resolution: "@react-email/components@npm:0.0.28"
-  dependencies:
-    "@react-email/body": "npm:0.0.10"
-    "@react-email/button": "npm:0.0.18"
-    "@react-email/code-block": "npm:0.0.10"
-    "@react-email/code-inline": "npm:0.0.4"
-    "@react-email/column": "npm:0.0.12"
-    "@react-email/container": "npm:0.0.14"
-    "@react-email/font": "npm:0.0.8"
-    "@react-email/head": "npm:0.0.11"
-    "@react-email/heading": "npm:0.0.14"
-    "@react-email/hr": "npm:0.0.10"
-    "@react-email/html": "npm:0.0.10"
-    "@react-email/img": "npm:0.0.10"
-    "@react-email/link": "npm:0.0.11"
-    "@react-email/markdown": "npm:0.0.12"
-    "@react-email/preview": "npm:0.0.11"
-    "@react-email/render": "npm:1.0.2"
-    "@react-email/row": "npm:0.0.11"
-    "@react-email/section": "npm:0.0.15"
-    "@react-email/tailwind": "npm:1.0.2"
-    "@react-email/text": "npm:0.0.10"
+  checksum: 10/5cdc383329caab109647b9c6269bfed7fad4e986eb41036392026a8b25a0265f46e8190e5ab7dffb943da36cce69e2918ec41774f598d793ee977911840348fe
+  languageName: node
+  linkType: hard
+
+"@react-email/components@npm:0.0.29":
+  version: 0.0.29
+  resolution: "@react-email/components@npm:0.0.29"
+  dependencies:
+    "@react-email/body": "npm:0.0.11"
+    "@react-email/button": "npm:0.0.19"
+    "@react-email/code-block": "npm:0.0.11"
+    "@react-email/code-inline": "npm:0.0.5"
+    "@react-email/column": "npm:0.0.13"
+    "@react-email/container": "npm:0.0.15"
+    "@react-email/font": "npm:0.0.9"
+    "@react-email/head": "npm:0.0.12"
+    "@react-email/heading": "npm:0.0.15"
+    "@react-email/hr": "npm:0.0.11"
+    "@react-email/html": "npm:0.0.11"
+    "@react-email/img": "npm:0.0.11"
+    "@react-email/link": "npm:0.0.12"
+    "@react-email/markdown": "npm:0.0.13"
+    "@react-email/preview": "npm:0.0.12"
+    "@react-email/render": "npm:1.0.3"
+    "@react-email/row": "npm:0.0.12"
+    "@react-email/section": "npm:0.0.16"
+    "@react-email/tailwind": "npm:1.0.3"
+    "@react-email/text": "npm:0.0.11"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/384f5b37d68cd59567810b4b46efdc18c4e709bd2224d257dfa71e33c92174e2c3f469da66436a3d26606a77bf2405cb12682d2ab9b8db939e6da12ce70104d0
+  checksum: 10/90c04ce3c2c7cad8eb2ae93375a10548c73313ac21114b6dcf2297154166749318c989d4e69379794efe07bc2fe822e681e99dae1e23ae74fd34b72eed325024
   languageName: node
   linkType: hard
 
-"@react-email/container@npm:0.0.14":
-  version: 0.0.14
-  resolution: "@react-email/container@npm:0.0.14"
+"@react-email/container@npm:0.0.15":
+  version: 0.0.15
+  resolution: "@react-email/container@npm:0.0.15"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/2dc58f162e41af49a1304216fc3e97a60ef4868670dd2aa0d860a8db2ac38622522ec4364bf9ac7541acb321baaaf76b29f8f7bfa3543c3360a584521eb48c6c
+  checksum: 10/10cd6eb35b02f7a871741183a28c84eb3f12b2aea0df8269e380a215a7907f8aff7b2b7639066377e56d72097eed52bba6a44c5cb92fecc2ef796d26b94f3430
   languageName: node
   linkType: hard
 
-"@react-email/font@npm:0.0.8":
-  version: 0.0.8
-  resolution: "@react-email/font@npm:0.0.8"
+"@react-email/font@npm:0.0.9":
+  version: 0.0.9
+  resolution: "@react-email/font@npm:0.0.9"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/ce93b03c29d721f6a93f63fdfa29590c96785b8e9689bdb01e70ee482ff43a4834096092b7f888ee750fb57fe8fed4f9c990dd5c63d22af12ee592ca8f1fb2db
+  checksum: 10/55ab760b82d3a09bd49c69d5d33d39270b69c6d419abe4a9ff790750f78f3dbf398a3b855beb242dc17f2908d54c103e52d2978b7134cbc51ac592ef567db9bc
   languageName: node
   linkType: hard
 
-"@react-email/head@npm:0.0.11":
-  version: 0.0.11
-  resolution: "@react-email/head@npm:0.0.11"
+"@react-email/head@npm:0.0.12":
+  version: 0.0.12
+  resolution: "@react-email/head@npm:0.0.12"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/2531af0b6dd0d3c258c6f287015ba008ac24bc219f2b25b0c8e8793d50ce67014405ef238eda41b3589fc10329e7337d2dcda881f3e097919cc6cbb13ea98378
+  checksum: 10/37a59ea76538f1f865f029cae75144d3bd9d74981a26e416a5fe9e4dd10b54a972ec1c4c4c8a72ecc240a991780bca713ff9fdd1054c600f6b8549d4430d8610
   languageName: node
   linkType: hard
 
-"@react-email/heading@npm:0.0.14":
-  version: 0.0.14
-  resolution: "@react-email/heading@npm:0.0.14"
+"@react-email/heading@npm:0.0.15":
+  version: 0.0.15
+  resolution: "@react-email/heading@npm:0.0.15"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/57214a826655f288ba98ab580ad51981f2d708040238290301c7dc9f353f56d8f84b0c4cdb6fd4d50908c83275e9abdf52eeb61bb47772a32791209b0844c2e7
+  checksum: 10/5a0f304b162fe61e854b606ee2c753bf49bd80cec24502ebec71cbd9efec4f8f14bc20f89a3adfd457e75b3cfc566a59c49eca29dc383c352d76038d0e376dab
   languageName: node
   linkType: hard
 
-"@react-email/hr@npm:0.0.10":
-  version: 0.0.10
-  resolution: "@react-email/hr@npm:0.0.10"
+"@react-email/hr@npm:0.0.11":
+  version: 0.0.11
+  resolution: "@react-email/hr@npm:0.0.11"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/a460e56a3e9aea23c04c087f6e7c35481ad778077f468c3572f44e5631b4efb20404655e96b20978ece8f170315382d82a33ed01e38758345159b16fee39f12b
+  checksum: 10/0a904a174d28adda494342021db5e7b05d830402fbb2f351b360a1775fee28e3dffe123e9286545c2c612c17165635e1d73b3c20a5785b696240b039631b19ab
   languageName: node
   linkType: hard
 
-"@react-email/html@npm:0.0.10":
-  version: 0.0.10
-  resolution: "@react-email/html@npm:0.0.10"
+"@react-email/html@npm:0.0.11":
+  version: 0.0.11
+  resolution: "@react-email/html@npm:0.0.11"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/e292bff395f0887625d8b9f708767666917cb3a4d4b2b5dcebd947437c53e25fd993e77c6ea5d61f8f751b3688f60972c7446060b610b0ace2d9230670df029e
+  checksum: 10/c302e38e0a6c059cbe42dab7473e24b099130f1ca8a4b7c231275dee1518b18426c62e87ad3a2f20df46d3844f9ecb7c6b27a106dfdb13e1e75289809ffe534a
   languageName: node
   linkType: hard
 
-"@react-email/img@npm:0.0.10":
-  version: 0.0.10
-  resolution: "@react-email/img@npm:0.0.10"
+"@react-email/img@npm:0.0.11":
+  version: 0.0.11
+  resolution: "@react-email/img@npm:0.0.11"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/be07ad4107903b34b52c8e699c24e5991c7890be50cea294a2a5d89a92209427fdf03c0ab6407254eb872269a816370d0e0c214a02a7a4916ea394eefb8a5bcb
+  checksum: 10/d85b1e42708c10b1f95e9cf9f74b7065d460db106b4b015ff71435891c38702e3fb5fc0b62d38521f93253b573645215ef4d5041202c05d79de59a1a2deda03e
   languageName: node
   linkType: hard
 
-"@react-email/link@npm:0.0.11":
-  version: 0.0.11
-  resolution: "@react-email/link@npm:0.0.11"
+"@react-email/link@npm:0.0.12":
+  version: 0.0.12
+  resolution: "@react-email/link@npm:0.0.12"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/ce858b4ee11fd88bf9e12a9705490b1869b10f7da1e7052b806a5bad087f2c0a0958af6168eaca8eeeefbec2409543e548b59a4e123dd2812b8a2bccc64bb0a8
+  checksum: 10/2c1c15ef363f49e50460288e3758c0c51b8b225992d8a4dae48d515392afa7354f4036da4884933306257ae776552f543068def067d3eeb95af0d1ef86b3cded
   languageName: node
   linkType: hard
 
-"@react-email/markdown@npm:0.0.12":
-  version: 0.0.12
-  resolution: "@react-email/markdown@npm:0.0.12"
+"@react-email/markdown@npm:0.0.13":
+  version: 0.0.13
+  resolution: "@react-email/markdown@npm:0.0.13"
   dependencies:
-    md-to-react-email: "npm:5.0.2"
+    md-to-react-email: "npm:5.0.4"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/808241dd363af5aca30c8df28c3aa1d8e20056d1d264eb0726b5a1339c87b7e990cd19bb0c9c7935e291cc456f0df1c9b80f9a264507139d212392ff64fa1298
+  checksum: 10/94518c8c707d822cca83afee362f05ab0aca316ca2f1d51bc52968e4270e070bd43984c80c2fe4939f919efac57e900bcf944452c4cb42bdd5cf76914803c565
   languageName: node
   linkType: hard
 
-"@react-email/preview@npm:0.0.11":
-  version: 0.0.11
-  resolution: "@react-email/preview@npm:0.0.11"
+"@react-email/preview@npm:0.0.12":
+  version: 0.0.12
+  resolution: "@react-email/preview@npm:0.0.12"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/661b1a5d2f68a224bcd58f2cab46141c14e964f1fced470a47cfa795cabe383568ac6c6ad3fc1b3f5191098ea236bd0559c541309e67c6b2e86af00c7a9ac565
+  checksum: 10/f8f18fc27e6691535e73f4a33628ee175d9f8f947402e2c92cbeb7f22ce9353a95e70fcbd6a85ca52373460adab6e89700ac5cfd3c18d7715625324f162e34b8
   languageName: node
   linkType: hard
 
-"@react-email/render@npm:1.0.2":
-  version: 1.0.2
-  resolution: "@react-email/render@npm:1.0.2"
+"@react-email/render@npm:1.0.3":
+  version: 1.0.3
+  resolution: "@react-email/render@npm:1.0.3"
   dependencies:
     html-to-text: "npm:9.0.5"
-    js-beautify: "npm:^1.14.11"
+    prettier: "npm:3.3.3"
     react-promise-suspense: "npm:0.3.4"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
     react-dom: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/f2fc9f579c95e14d1bb721c46eb37473ff7989d895a05fce72c611d2c0eae21365f9bc0463d7f88feb99af2c9c163f3e3a8a3f597fdb74c5f53ea536dbb4a480
+  checksum: 10/01f26433ea50548e61aa5a3f3ad41e3aabc514147bce4157417c5e9c280566b150328e4cce65c0fb8d382cc9bd421a8ab4b1883732d4ef7b3c84212268753dcd
   languageName: node
   linkType: hard
 
-"@react-email/row@npm:0.0.11":
-  version: 0.0.11
-  resolution: "@react-email/row@npm:0.0.11"
+"@react-email/row@npm:0.0.12":
+  version: 0.0.12
+  resolution: "@react-email/row@npm:0.0.12"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/aac3942f47758fc7c8a264dde4a6026719cb09ff22d512d7185740a868d98d7ce0322efa77a9b68811d22b149897fce9fb89f349083718c34e6a7ebb750db97a
+  checksum: 10/8290dac2661920ce1b8c10d22a9cb69c1b45b43af35a79c05250a7e20d165d232154932d820aa2cf863ff3020b1d51344eaf6bee0ac4aeb750b8ebc012d0a91e
   languageName: node
   linkType: hard
 
-"@react-email/section@npm:0.0.15":
-  version: 0.0.15
-  resolution: "@react-email/section@npm:0.0.15"
+"@react-email/section@npm:0.0.16":
+  version: 0.0.16
+  resolution: "@react-email/section@npm:0.0.16"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/ff8e7d81c3cc825c17daa25717cd952f7d26fb0657df1f4edb1f8feca10229d5188ad2f1c4df4179cf4381d21c75221d7d6f66e86ddf1d7ba049986c556d11e0
+  checksum: 10/0dc243d06d15d234760d9c4e5450f8bf98ebf8c4447caa1d804d740453d0d5d373233f3eda74f31fc947041856d040fe465114b07e9d658333162a2f99992665
   languageName: node
   linkType: hard
 
-"@react-email/tailwind@npm:1.0.2":
-  version: 1.0.2
-  resolution: "@react-email/tailwind@npm:1.0.2"
+"@react-email/tailwind@npm:1.0.3":
+  version: 1.0.3
+  resolution: "@react-email/tailwind@npm:1.0.3"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/200b6cec6754bf51f41374387f30476f0321e04413c3a8003f1c1476f7ceb9b2b7d1c5217b753cb4e04523953a579ae5d27e5d00182ecf34cf03ac629c13e2f3
+  checksum: 10/8fb2ed83610835bce95fbcedb1154548c6d2fb30f15e4944255e40a2b7e58134de9fb0a4034b3616a301f84746613fd328804e10787ed0c5fb9371e8ae18bbdb
   languageName: node
   linkType: hard
 
-"@react-email/text@npm:0.0.10":
-  version: 0.0.10
-  resolution: "@react-email/text@npm:0.0.10"
+"@react-email/text@npm:0.0.11":
+  version: 0.0.11
+  resolution: "@react-email/text@npm:0.0.11"
   peerDependencies:
     react: ^18.0 || ^19.0 || ^19.0.0-rc
-  checksum: 10/b4deb47df62b4c8ecb230debc478331cac650c684b819a11ef930fd4b9700c5a9d408bf4cca27d07638891bafd22c7c35730d434221f0d2d89dde1b01a5d2ad6
+  checksum: 10/b8018f44e4b8c7797905599a47e9c8b2c29d8445675a0740a45b5e6c3f0bf443aefff55d78637134e60e38cee851c342e58e3241b50dfb5e35d83e3f47ad89a6
   languageName: node
   linkType: hard
 
@@ -5740,8 +5740,8 @@ __metadata:
     "@ryot/graphql": "workspace:*"
     "@ryot/ts-utils": "workspace:*"
     "@tabler/icons-react": "npm:3.17.0"
-    "@tanstack/react-query": "npm:5.62.0"
-    "@tanstack/react-query-devtools": "npm:5.62.0"
+    "@tanstack/react-query": "npm:5.62.2"
+    "@tanstack/react-query-devtools": "npm:5.62.2"
     "@types/cookie": "npm:1.0.0"
     "@types/crypto-js": "npm:4.2.2"
     "@types/howler": "npm:2.2.12"
@@ -5754,12 +5754,13 @@ __metadata:
     cookie: "npm:1.0.2"
     crypto-js: "npm:4.2.0"
     dayjs: "npm:1.11.13"
-    dotenv: "npm:16.4.5"
+    dotenv: "npm:16.4.7"
     embla-carousel-react: "npm:7.1.0"
     filesize: "npm:10.1.6"
     graphql: "npm:16.9.0"
     graphql-request: "npm:7.1.2"
     howler: "npm:2.2.4"
+    html2canvas: "npm:1.4.1"
     humanize-duration-ts: "npm:2.1.1"
     immer: "npm:10.1.1"
     isbot: "npm:5.1.17"
@@ -5774,9 +5775,9 @@ __metadata:
     react: "npm:18.3.1"
     react-confirm: "npm:0.3.0-7"
     react-dom: "npm:18.3.1"
-    react-virtuoso: "npm:4.12.2"
+    react-virtuoso: "npm:4.12.3"
     react-webcam: "npm:7.2.0"
-    recharts: "npm:2.13.3"
+    recharts: "npm:2.14.1"
     remix-development-tools: "npm:4.7.7"
     remix-routes: "npm:1.7.7"
     remix-utils: "npm:7.7.0"
@@ -5831,13 +5832,13 @@ __metadata:
   version: 0.0.0-use.local
   resolution: "@ryot/transactional@workspace:libs/transactional"
   dependencies:
-    "@react-email/components": "npm:0.0.28"
+    "@react-email/components": "npm:0.0.29"
     "@ryot/ts-utils": "workspace:*"
     "@types/react": "npm:18.3.12"
     "@types/react-dom": "npm:18.3.1"
     react: "npm:18.3.1"
     react-dom: "npm:18.3.1"
-    react-email: "npm:3.0.2"
+    react-email: "npm:3.0.3"
   languageName: unknown
   linkType: soft
 
@@ -5869,7 +5870,7 @@ __metadata:
     "@radix-ui/react-label": "npm:2.1.0"
     "@radix-ui/react-slot": "npm:1.1.0"
     "@radix-ui/react-tooltip": "npm:1.1.4"
-    "@react-email/components": "npm:0.0.28"
+    "@react-email/components": "npm:0.0.29"
     "@remix-run/dev": "npm:2.15.0"
     "@remix-run/node": "npm:2.15.0"
     "@remix-run/react": "npm:2.15.0"
@@ -5891,8 +5892,8 @@ __metadata:
     clsx: "npm:2.1.1"
     crypto-js: "npm:4.2.0"
     dayjs: "npm:1.11.13"
-    drizzle-kit: "npm:0.28.1"
-    drizzle-orm: "npm:0.36.4"
+    drizzle-kit: "npm:0.29.1"
+    drizzle-orm: "npm:0.37.0"
     embla-carousel-autoplay: "npm:8.5.1"
     embla-carousel-react: "npm:8.5.1"
     graphql: "npm:16.9.0"
@@ -5901,7 +5902,7 @@ __metadata:
     input-otp: "npm:1.4.1"
     isbot: "npm:5.1.17"
     lodash: "npm:4.17.21"
-    lucide-react: "npm:0.462.0"
+    lucide-react: "npm:0.465.0"
     nanoid: "npm:5.0.9"
     next-themes: "npm:0.4.3"
     nodemailer: "npm:6.9.16"
@@ -5914,7 +5915,7 @@ __metadata:
     remix-utils: "npm:7.7.0"
     sonner: "npm:1.7.0"
     tailwind-merge: "npm:2.5.5"
-    tailwindcss: "npm:3.4.15"
+    tailwindcss: "npm:3.4.16"
     tailwindcss-animate: "npm:1.0.7"
     tiny-invariant: "npm:1.3.3"
     ts-pattern: "npm:5.5.0"
@@ -5994,10 +5995,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@tanstack/query-core@npm:5.62.0":
-  version: 5.62.0
-  resolution: "@tanstack/query-core@npm:5.62.0"
-  checksum: 10/f3172cd997907ef2b3c767ee51116fffd739f26b504f2d3ce5854cc118c4e2ad1bd1d1d5f223c5ed431fb90f96e649425572402b580852b6ee7aaee72a2dac12
+"@tanstack/query-core@npm:5.62.2":
+  version: 5.62.2
+  resolution: "@tanstack/query-core@npm:5.62.2"
+  checksum: 10/ea5c5c69b93e62cc89723d20fc2c5a1cee1190f6e4d185dfef266d6af917c409ed58ce977bc7cd2d5371b96e02bc61f848578e25be3a8d8e5f95c607abc34e4e
   languageName: node
   linkType: hard
 
@@ -6008,26 +6009,26 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@tanstack/react-query-devtools@npm:5.62.0":
-  version: 5.62.0
-  resolution: "@tanstack/react-query-devtools@npm:5.62.0"
+"@tanstack/react-query-devtools@npm:5.62.2":
+  version: 5.62.2
+  resolution: "@tanstack/react-query-devtools@npm:5.62.2"
   dependencies:
     "@tanstack/query-devtools": "npm:5.61.4"
   peerDependencies:
-    "@tanstack/react-query": ^5.62.0
+    "@tanstack/react-query": ^5.62.2
     react: ^18 || ^19
-  checksum: 10/cabc9b7b4b7e8c16bec422e4fb969e3190632cdab41197f5401515003d9977a052c1651dca211f6a11931daea0f2dc315746366bbf507d026b83ce6994da5c9b
+  checksum: 10/e06e00b5ecab01c8b775308626bb882d8cda7d1c122c000fe88865e640caa326ca6ba0a4e1fdf2ad9a7a1966fe610e626d392509b7e5344c7d0cff8678668c6b
   languageName: node
   linkType: hard
 
-"@tanstack/react-query@npm:5.62.0":
-  version: 5.62.0
-  resolution: "@tanstack/react-query@npm:5.62.0"
+"@tanstack/react-query@npm:5.62.2":
+  version: 5.62.2
+  resolution: "@tanstack/react-query@npm:5.62.2"
   dependencies:
-    "@tanstack/query-core": "npm:5.62.0"
+    "@tanstack/query-core": "npm:5.62.2"
   peerDependencies:
     react: ^18 || ^19
-  checksum: 10/69712c822e35a32a27fc06009d7e12904e72369bd5dbc039f7fd1023811440f60a9df096e22196a53a64564b6bebfcae755de4b468ce0056e8092f769b0bac52
+  checksum: 10/2d449d8fc1935a3b3ce4d3092feab3317e58db95160bc6cc11781878c703fa67199c559b71be5882285f5829e8dc398a58fc6409ba4ee770ef1398849a006c1c
   languageName: node
   linkType: hard
 
@@ -6919,6 +6920,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"base64-arraybuffer@npm:^1.0.2":
+  version: 1.0.2
+  resolution: "base64-arraybuffer@npm:1.0.2"
+  checksum: 10/15e6400d2d028bf18be4ed97702b11418f8f8779fb8c743251c863b726638d52f69571d4cc1843224da7838abef0949c670bde46936663c45ad078e89fee5c62
+  languageName: node
+  linkType: hard
+
 "base64-js@npm:^1.3.1":
   version: 1.5.1
   resolution: "base64-js@npm:1.5.1"
@@ -7963,6 +7971,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"css-line-break@npm:^2.1.0":
+  version: 2.1.0
+  resolution: "css-line-break@npm:2.1.0"
+  dependencies:
+    utrie: "npm:^1.0.2"
+  checksum: 10/e75cae40de511026228d4fa69e8d464895714f8899880b8268a446b57f0faa84b490ba1bdda5ed9e7f38f99ab947c6bc941bb505d8119c49072db079c1cacea5
+  languageName: node
+  linkType: hard
+
 "css-what@npm:^6.1.0":
   version: 6.1.0
   resolution: "css-what@npm:6.1.0"
@@ -8485,10 +8502,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"dotenv@npm:16.4.5":
-  version: 16.4.5
-  resolution: "dotenv@npm:16.4.5"
-  checksum: 10/55a3134601115194ae0f924e54473459ed0d9fc340ae610b676e248cca45aa7c680d86365318ea964e6da4e2ea80c4514c1adab5adb43d6867fb57ff068f95c8
+"dotenv@npm:16.4.7":
+  version: 16.4.7
+  resolution: "dotenv@npm:16.4.7"
+  checksum: 10/f13bfe97db88f0df4ec505eeffb8925ec51f2d56a3d0b6d916964d8b4af494e6fb1633ba5d09089b552e77ab2a25de58d70259b2c5ed45ec148221835fc99a0c
   languageName: node
   linkType: hard
 
@@ -8506,9 +8523,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"drizzle-kit@npm:0.28.1":
-  version: 0.28.1
-  resolution: "drizzle-kit@npm:0.28.1"
+"drizzle-kit@npm:0.29.1":
+  version: 0.29.1
+  resolution: "drizzle-kit@npm:0.29.1"
   dependencies:
     "@drizzle-team/brocli": "npm:^0.10.2"
     "@esbuild-kit/esm-loader": "npm:^2.5.5"
@@ -8516,16 +8533,16 @@ __metadata:
     esbuild-register: "npm:^3.5.0"
   bin:
     drizzle-kit: bin.cjs
-  checksum: 10/f02e2b63a080cc50341c566718f051cc2813b3965f65072e5d7e3ffe3b577b381c7abbc60fc985e70f7cb5be68940a83dc9b28839dcd6c9e0bed3e957f3faae9
+  checksum: 10/5e6041de38054384b9890ef5de4cffdec2e72897e648a04b53d4ad0a0d0451ebc436941237cdbc5aa49bf7c9181e2a1483413068fa819cf91133d085f716c2b4
   languageName: node
   linkType: hard
 
-"drizzle-orm@npm:0.36.4":
-  version: 0.36.4
-  resolution: "drizzle-orm@npm:0.36.4"
+"drizzle-orm@npm:0.37.0":
+  version: 0.37.0
+  resolution: "drizzle-orm@npm:0.37.0"
   peerDependencies:
     "@aws-sdk/client-rds-data": ">=3"
-    "@cloudflare/workers-types": ">=3"
+    "@cloudflare/workers-types": ">=4"
     "@electric-sql/pglite": ">=0.2.0"
     "@libsql/client": ">=0.10.0"
     "@libsql/client-wasm": ">=0.10.0"
@@ -8611,7 +8628,7 @@ __metadata:
       optional: true
     sqlite3:
       optional: true
-  checksum: 10/455473db5b7db188f3bd1638e8e50a0244995b564d2ae72ad698c1e96681e1a91433c1b3f66d2798018d3d61eb6b752eb6c714fa8f3938d684d2c593980b99fe
+  checksum: 10/d9253fedca1172dee78a6a47fe08a115f6284638e30c7eb89233d41f646d8f36f2646592331f54bb175af2c502000102779caed232b21764938a4a2cf110eabc
   languageName: node
   linkType: hard
 
@@ -10463,6 +10480,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"html2canvas@npm:1.4.1":
+  version: 1.4.1
+  resolution: "html2canvas@npm:1.4.1"
+  dependencies:
+    css-line-break: "npm:^2.1.0"
+    text-segmentation: "npm:^1.0.3"
+  checksum: 10/595790810557a1d4287f07b6ead49aed4f169f08eb00e20c1f030b93344003e84797d28cd8a220e8ec1b78641d460ca6add11b8531960725526f11a7b9fa9900
+  languageName: node
+  linkType: hard
+
 "html@npm:^1.0.0":
   version: 1.0.0
   resolution: "html@npm:1.0.0"
@@ -11189,7 +11216,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"js-beautify@npm:^1.14.11, js-beautify@npm:^1.6.4":
+"js-beautify@npm:^1.6.4":
   version: 1.15.1
   resolution: "js-beautify@npm:1.15.1"
   dependencies:
@@ -11388,7 +11415,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"lilconfig@npm:^2.0.5, lilconfig@npm:^2.1.0":
+"lilconfig@npm:^2.0.5":
   version: 2.1.0
   resolution: "lilconfig@npm:2.1.0"
   checksum: 10/b1314a2e55319013d5e7d7d08be39015829d2764a1eaee130129545d40388499d81b1c31b0f9b3417d4db12775a88008b72ec33dd06e0184cf7503b32ca7cc0b
@@ -11402,6 +11429,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"lilconfig@npm:^3.1.3":
+  version: 3.1.3
+  resolution: "lilconfig@npm:3.1.3"
+  checksum: 10/b932ce1af94985f0efbe8896e57b1f814a48c8dbd7fc0ef8469785c6303ed29d0090af3ccad7e36b626bfca3a4dc56cc262697e9a8dd867623cf09a39d54e4c3
+  languageName: node
+  linkType: hard
+
 "lines-and-columns@npm:^1.1.6":
   version: 1.2.4
   resolution: "lines-and-columns@npm:1.2.4"
@@ -11608,12 +11642,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"lucide-react@npm:0.462.0":
-  version: 0.462.0
-  resolution: "lucide-react@npm:0.462.0"
+"lucide-react@npm:0.465.0":
+  version: 0.465.0
+  resolution: "lucide-react@npm:0.465.0"
   peerDependencies:
     react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc
-  checksum: 10/a9f4d1eae10d6bcd02de6e81f9aba4286fe734c0fe383437e1e6964a2cd8efaabacc38197b7f39eaf4e44cc71103533158b1a7ae06bdcc40506f090380e8b872
+  checksum: 10/9f69bdd198dc6708fc135bb328fb4e5ba48c9f85864124f5a8b6842fd7fbac8cb4afe69d30fd89e31b2a94da9b083f552ab71d7845b1ae84ac0fdec9a43269e2
   languageName: node
   linkType: hard
 
@@ -11709,14 +11743,14 @@ __metadata:
   languageName: node
   linkType: hard
 
-"md-to-react-email@npm:5.0.2":
-  version: 5.0.2
-  resolution: "md-to-react-email@npm:5.0.2"
+"md-to-react-email@npm:5.0.4":
+  version: 5.0.4
+  resolution: "md-to-react-email@npm:5.0.4"
   dependencies:
     marked: "npm:7.0.4"
   peerDependencies:
     react: 18.x
-  checksum: 10/d63af34d747d64008c582d8367eb0e5cdc4dcd8effe8dc27329c4112f888bd7cd468ff85a6a9b6f467b995b77a56ac0535819e980abaee15cdc4ba8ca3feedc2
+  checksum: 10/599606de1f73f094666bc83b4c5bee32be9a2cb32a9f9f37adb5c7fa2708a1d281c6400e1805951bc4851210ea3d217dcbbd0a079f52e3ee71767cd7c8ac8bec
   languageName: node
   linkType: hard
 
@@ -12675,20 +12709,20 @@ __metadata:
   languageName: node
   linkType: hard
 
-"next@npm:14.2.10":
-  version: 14.2.10
-  resolution: "next@npm:14.2.10"
+"next@npm:14.2.16":
+  version: 14.2.16
+  resolution: "next@npm:14.2.16"
   dependencies:
-    "@next/env": "npm:14.2.10"
-    "@next/swc-darwin-arm64": "npm:14.2.10"
-    "@next/swc-darwin-x64": "npm:14.2.10"
-    "@next/swc-linux-arm64-gnu": "npm:14.2.10"
-    "@next/swc-linux-arm64-musl": "npm:14.2.10"
-    "@next/swc-linux-x64-gnu": "npm:14.2.10"
-    "@next/swc-linux-x64-musl": "npm:14.2.10"
-    "@next/swc-win32-arm64-msvc": "npm:14.2.10"
-    "@next/swc-win32-ia32-msvc": "npm:14.2.10"
-    "@next/swc-win32-x64-msvc": "npm:14.2.10"
+    "@next/env": "npm:14.2.16"
+    "@next/swc-darwin-arm64": "npm:14.2.16"
+    "@next/swc-darwin-x64": "npm:14.2.16"
+    "@next/swc-linux-arm64-gnu": "npm:14.2.16"
+    "@next/swc-linux-arm64-musl": "npm:14.2.16"
+    "@next/swc-linux-x64-gnu": "npm:14.2.16"
+    "@next/swc-linux-x64-musl": "npm:14.2.16"
+    "@next/swc-win32-arm64-msvc": "npm:14.2.16"
+    "@next/swc-win32-ia32-msvc": "npm:14.2.16"
+    "@next/swc-win32-x64-msvc": "npm:14.2.16"
     "@swc/helpers": "npm:0.5.5"
     busboy: "npm:1.6.0"
     caniuse-lite: "npm:^1.0.30001579"
@@ -12729,7 +12763,7 @@ __metadata:
       optional: true
   bin:
     next: dist/bin/next
-  checksum: 10/73b254332a04d41885aa63b23d692d8e1c665de4db383b29363292729b128c22ade07cae1fd1e088a9cbb595106047397bbf92bc769b124f4069844833c0a06c
+  checksum: 10/819e28509649ad290647f16b02b5a051c568892a4255a7606bed0b253466529bd9caf6947233c3b3643c3c6d14b95e8e7a74f793baeb223c3519e8eaa36aaa2a
   languageName: node
   linkType: hard
 
@@ -13793,6 +13827,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"prettier@npm:3.3.3":
+  version: 3.3.3
+  resolution: "prettier@npm:3.3.3"
+  bin:
+    prettier: bin/prettier.cjs
+  checksum: 10/5beac1f30b5b40162532b8e2f7c3a4eb650910a2695e9c8512a62ffdc09dae93190c29db9107fa7f26d1b6c71aad3628ecb9b5de1ecb0911191099be109434d7
+  languageName: node
+  linkType: hard
+
 "prettier@npm:^2.7.1":
   version: 2.8.8
   resolution: "prettier@npm:2.8.8"
@@ -14052,9 +14095,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"react-email@npm:3.0.2":
-  version: 3.0.2
-  resolution: "react-email@npm:3.0.2"
+"react-email@npm:3.0.3":
+  version: 3.0.3
+  resolution: "react-email@npm:3.0.3"
   dependencies:
     "@babel/core": "npm:7.24.5"
     "@babel/parser": "npm:7.24.5"
@@ -14066,13 +14109,13 @@ __metadata:
     glob: "npm:10.3.4"
     log-symbols: "npm:4.1.0"
     mime-types: "npm:2.1.35"
-    next: "npm:14.2.10"
+    next: "npm:14.2.16"
     normalize-path: "npm:3.0.0"
     ora: "npm:5.4.1"
     socket.io: "npm:4.8.0"
   bin:
     email: dist/cli/index.js
-  checksum: 10/c294b8d9b56bb3721e903bacb8799172d2e6b6bcaf0f3f61174beebc85ed138f5fe2f3cd4e1cb589cdf49149f2282220b9451be7c89f46930820e7062d965aab
+  checksum: 10/14375647f25131caa9744f6487b770dc352e0a5263c707a21cd1638b9d424f00eba7bc032f4db6b2d57bdc0da1a3cd620c8eb39052f7b0ff7ae51a19555d9a03
   languageName: node
   linkType: hard
 
@@ -14289,13 +14332,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"react-virtuoso@npm:4.12.2":
-  version: 4.12.2
-  resolution: "react-virtuoso@npm:4.12.2"
+"react-virtuoso@npm:4.12.3":
+  version: 4.12.3
+  resolution: "react-virtuoso@npm:4.12.3"
   peerDependencies:
     react: ">=16 || >=17 || >= 18"
     react-dom: ">=16 || >=17 || >= 18"
-  checksum: 10/7dc85f74d9eca7a51a80b34d3c2a541e46e1601254408ff98cdd6f2ec6eb4552a5c94c7cc140b98daac680bd2dea27e3382be9778714151ca8c28b20e53fab32
+  checksum: 10/6e43800c23b8f12dba9e304462410dc2bfe82eecfa0bea97c89929a7d14d469709f69b0ba7da4e37de6bc98dacecd656fea208a1ef05cae579bb5a41181607cd
   languageName: node
   linkType: hard
 
@@ -14401,9 +14444,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"recharts@npm:2.13.3":
-  version: 2.13.3
-  resolution: "recharts@npm:2.13.3"
+"recharts@npm:2.14.1":
+  version: 2.14.1
+  resolution: "recharts@npm:2.14.1"
   dependencies:
     clsx: "npm:^2.0.0"
     eventemitter3: "npm:^4.0.1"
@@ -14416,7 +14459,7 @@ __metadata:
   peerDependencies:
     react: ^16.0.0 || ^17.0.0 || ^18.0.0
     react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0
-  checksum: 10/0871fbd281c341a45700a7b8d585e5aae6b77d7e8d5fa810ff92c3f0256f905c8d9eaab15be29cd6aa34dde6e899d13a91964a08ea66a643b6884f7374f3060d
+  checksum: 10/b64750be74fc8d2cb41570b86711a1309db330de188c3b3b68c74c112349bd36ff3f2234ea7ccf3571cc12d4a53e7c17a35dee3818ac140adb515f6ea25f6a01
   languageName: node
   linkType: hard
 
@@ -15639,9 +15682,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"tailwindcss@npm:3.4.15":
-  version: 3.4.15
-  resolution: "tailwindcss@npm:3.4.15"
+"tailwindcss@npm:3.4.16":
+  version: 3.4.16
+  resolution: "tailwindcss@npm:3.4.16"
   dependencies:
     "@alloc/quick-lru": "npm:^5.2.0"
     arg: "npm:^5.0.2"
@@ -15652,7 +15695,7 @@ __metadata:
     glob-parent: "npm:^6.0.2"
     is-glob: "npm:^4.0.3"
     jiti: "npm:^1.21.6"
-    lilconfig: "npm:^2.1.0"
+    lilconfig: "npm:^3.1.3"
     micromatch: "npm:^4.0.8"
     normalize-path: "npm:^3.0.0"
     object-hash: "npm:^3.0.0"
@@ -15668,7 +15711,7 @@ __metadata:
   bin:
     tailwind: lib/cli.js
     tailwindcss: lib/cli.js
-  checksum: 10/670bc70ae98d36601990424067f1164eebcbe97493ce8d314e8baa85eb47a6b92caabcea3046dfa9e3888a40a4807c123e12dfa2e05d884ff0583c6a1b647cb7
+  checksum: 10/d84b3d9bd8f3d53021b6754e3d7efa704cf32f72714dea2036d955fe46ea4154180d2c47593881a9c524229f9efc13fa924fa6347fc8969427036329ee8a9338
   languageName: node
   linkType: hard
 
@@ -15711,6 +15754,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"text-segmentation@npm:^1.0.3":
+  version: 1.0.3
+  resolution: "text-segmentation@npm:1.0.3"
+  dependencies:
+    utrie: "npm:^1.0.2"
+  checksum: 10/86191de83f09b96f356628c3dbaf6d281eda46a4dd4c94c3827495428871b9dd44ead4ef4cdf06a188b08a3b83e3943c5911841422b55286b121ba9e04c846dd
+  languageName: node
+  linkType: hard
+
 "thenify-all@npm:^1.0.0":
   version: 1.6.0
   resolution: "thenify-all@npm:1.6.0"
@@ -16465,6 +16517,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"utrie@npm:^1.0.2":
+  version: 1.0.2
+  resolution: "utrie@npm:1.0.2"
+  dependencies:
+    base64-arraybuffer: "npm:^1.0.2"
+  checksum: 10/0c9458380bf3113f425a268e3d605aef163bfbaea62bf24de517b62b6e6744394d8ef1cdd9b07423359aec5ed402baab4bb2c5beec64f674ec635dc2f8dbd4bf
+  languageName: node
+  linkType: hard
+
 "uuid@npm:11.0.3":
   version: 11.0.3
   resolution: "uuid@npm:11.0.3"


Test patch:
diff --git a/libs/graphql/src/backend/queries/LatestUserSummary.gql b/libs/graphql/src/backend/queries/LatestUserSummary.gql
deleted file mode 100644
index 23fc3cab31..0000000000
--- a/libs/graphql/src/backend/queries/LatestUserSummary.gql
+++ /dev/null
@@ -1,36 +0,0 @@
-query LatestUserSummary {
-	latestUserSummary {
-		totalMetadataReviewCount
-		totalCollectionReviewCount
-		totalMetadataGroupReviewCount
-		totalPersonReviewCount
-		measurementCount
-		workoutCount
-		totalWorkoutDuration
-		audioBookCount
-		totalAudioBookDuration
-		animeCount
-		bookCount
-		totalBookPages
-		podcastCount
-		totalPodcastDuration
-		mangaCount
-		movieCount
-		totalMovieDuration
-		showCount
-		totalShowDuration
-		videoGameCount
-		totalVideoGameDuration
-		visualNovelCount
-		totalVisualNovelDuration
-		totalWorkoutPersonalBests
-		totalWorkoutWeight
-		totalWorkoutReps
-		totalWorkoutDistance
-		totalWorkoutRestTime
-		totalMetadataCount
-		totalReviewCount
-		totalCount
-		totalDuration
-	}
-}