Repository: IgnisDa/ryot (language: rust)

Issue:
Youtube music integration



Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index ab1e344c82..3118292bbf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -45,7 +45,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
 dependencies = [
  "cfg-if",
- "getrandom",
  "once_cell",
  "version_check",
  "zerocopy 0.7.35",
@@ -83,9 +82,9 @@ dependencies = [
 
 [[package]]
 name = "allocator-api2"
-version = "0.2.20"
+version = "0.2.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9"
+checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
 
 [[package]]
 name = "android-tzdata"
@@ -153,9 +152,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.93"
+version = "1.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
+checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
 
 [[package]]
 name = "apalis"
@@ -167,7 +166,7 @@ dependencies = [
  "futures",
  "pin-project-lite",
  "serde",
- "thiserror 2.0.3",
+ "thiserror 2.0.9",
  "tower",
  "tracing",
  "tracing-futures",
@@ -183,7 +182,7 @@ dependencies = [
  "futures-timer",
  "pin-project-lite",
  "serde",
- "thiserror 2.0.3",
+ "thiserror 2.0.9",
  "tower",
  "ulid",
 ]
@@ -216,7 +215,7 @@ dependencies = [
  "file-storage-service",
  "jwt-service",
  "media-models",
- "reqwest 0.12.9",
+ "reqwest 0.12.12",
  "sea-orm",
 ]
 
@@ -238,12 +237,6 @@ version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
 
-[[package]]
-name = "ascii"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e"
-
 [[package]]
 name = "ascii_utils"
 version = "0.9.3"
@@ -276,7 +269,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "serde",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -310,9 +303,9 @@ dependencies = [
 
 [[package]]
 name = "async-graphql"
-version = "7.0.11"
+version = "7.0.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ba6d24703c5adc5ba9116901b92ee4e4c0643c01a56c4fd303f3818638d7449"
+checksum = "59fd6bd734afb8b6e4d0f84a3e77305ce0a7ccc60d70f6001cb5e1c3f38d8ff1"
 dependencies = [
  "async-graphql-derive",
  "async-graphql-parser",
@@ -327,13 +320,12 @@ dependencies = [
  "futures-timer",
  "futures-util",
  "handlebars",
- "http 1.1.0",
+ "http 1.2.0",
  "indexmap 2.7.0",
  "log",
  "mime",
  "multer",
  "num-traits",
- "once_cell",
  "pin-project-lite",
  "regex",
  "rust_decimal",
@@ -349,9 +341,9 @@ dependencies = [
 
 [[package]]
 name = "async-graphql-axum"
-version = "7.0.11"
+version = "7.0.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9aa80e171205c6d562057fd5a49167c8fbe61f7db2bed6540f6d4f2234d7ff2"
+checksum = "ec8c1bb47161c37286e40e2fa58055e97b2a2b6cf1022a6686967e10636fa5d7"
 dependencies = [
  "async-graphql",
  "async-trait",
@@ -367,9 +359,9 @@ dependencies = [
 
 [[package]]
 name = "async-graphql-derive"
-version = "7.0.11"
+version = "7.0.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a94c2d176893486bd37cd1b6defadd999f7357bf5804e92f510c08bcf16c538f"
+checksum = "ac38b4dd452d529d6c0248b51df23603f0a875770352e26ae8c346ce6c149b3e"
 dependencies = [
  "Inflector",
  "async-graphql-parser",
@@ -378,15 +370,15 @@ dependencies = [
  "proc-macro2",
  "quote",
  "strum",
- "syn 2.0.90",
+ "syn 2.0.92",
  "thiserror 1.0.69",
 ]
 
 [[package]]
 name = "async-graphql-parser"
-version = "7.0.11"
+version = "7.0.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79272bdbf26af97866e149f05b2b546edb5c00e51b5f916289931ed233e208ad"
+checksum = "42d271ddda2f55b13970928abbcbc3423cfc18187c60e8769b48f21a93b7adaa"
 dependencies = [
  "async-graphql-value",
  "pest",
@@ -396,9 +388,9 @@ dependencies = [
 
 [[package]]
 name = "async-graphql-value"
-version = "7.0.11"
+version = "7.0.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef5ec94176a12a8cbe985cd73f2e54dc9c702c88c766bdef12f1f3a67cedbee1"
+checksum = "aefe909173a037eaf3281b046dc22580b59a38b765d7b8d5116f2ffef098048d"
 dependencies = [
  "bytes",
  "indexmap 2.7.0",
@@ -425,7 +417,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -436,7 +428,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -474,9 +466,9 @@ dependencies = [
 
 [[package]]
 name = "aws-runtime"
-version = "1.4.3"
+version = "1.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a10d5c055aa540164d9561a0e2e74ad30f0dcf7393c3a92f6733ddf9c5762468"
+checksum = "44f6f1124d6e19ab6daf7f2e615644305dc6cb2d706892a8a8c0b98db35de020"
 dependencies = [
  "aws-credential-types",
  "aws-sigv4",
@@ -500,9 +492,9 @@ dependencies = [
 
 [[package]]
 name = "aws-sdk-s3"
-version = "1.63.0"
+version = "1.67.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f43850204a109a5eea1ea93951cf0440268cef98b0d27dfef4534949e23735f7"
+checksum = "bbc644164269a1e38ce7f2f7373629d3fb3d310c0e3feb5573a29744288b24d3"
 dependencies = [
  "aws-credential-types",
  "aws-runtime",
@@ -534,9 +526,9 @@ dependencies = [
 
 [[package]]
 name = "aws-sigv4"
-version = "1.2.5"
+version = "1.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5619742a0d8f253be760bfbb8e8e8368c69e3587e4637af5754e488a611499b1"
+checksum = "7d3820e0c08d0737872ff3c7c1f21ebbb6693d832312d6152bf18ef50a5471c2"
 dependencies = [
  "aws-credential-types",
  "aws-smithy-eventstream",
@@ -549,7 +541,7 @@ dependencies = [
  "hex",
  "hmac",
  "http 0.2.12",
- "http 1.1.0",
+ "http 1.2.0",
  "once_cell",
  "p256 0.11.1",
  "percent-encoding",
@@ -563,9 +555,9 @@ dependencies = [
 
 [[package]]
 name = "aws-smithy-async"
-version = "1.2.1"
+version = "1.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62220bc6e97f946ddd51b5f1361f78996e704677afc518a4ff66b7a72ea1378c"
+checksum = "427cb637d15d63d6f9aae26358e1c9a9c09d5aa490d64b09354c8217cfef0f28"
 dependencies = [
  "futures-util",
  "pin-project-lite",
@@ -627,18 +619,18 @@ dependencies = [
 
 [[package]]
 name = "aws-smithy-json"
-version = "0.60.7"
+version = "0.61.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4683df9469ef09468dad3473d129960119a0d3593617542b7d52086c8486f2d6"
+checksum = "ee4e69cc50921eb913c6b662f8d909131bb3e6ad6cb6090d3a39b66fc5c52095"
 dependencies = [
  "aws-smithy-types",
 ]
 
 [[package]]
 name = "aws-smithy-runtime"
-version = "1.7.3"
+version = "1.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be28bd063fa91fd871d131fc8b68d7cd4c5fa0869bea68daca50dcb1cbd76be2"
+checksum = "a05dd41a70fc74051758ee75b5c4db2c0ca070ed9229c3df50e9475cda1cb985"
 dependencies = [
  "aws-smithy-async",
  "aws-smithy-http",
@@ -651,7 +643,7 @@ dependencies = [
  "http-body 0.4.6",
  "http-body 1.0.1",
  "httparse",
- "hyper 0.14.31",
+ "hyper 0.14.32",
  "hyper-rustls 0.24.2",
  "once_cell",
  "pin-project-lite",
@@ -671,7 +663,7 @@ dependencies = [
  "aws-smithy-types",
  "bytes",
  "http 0.2.12",
- "http 1.1.0",
+ "http 1.2.0",
  "pin-project-lite",
  "tokio",
  "tracing",
@@ -680,16 +672,16 @@ dependencies = [
 
 [[package]]
 name = "aws-smithy-types"
-version = "1.2.9"
+version = "1.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fbd94a32b3a7d55d3806fe27d98d3ad393050439dd05eb53ece36ec5e3d3510"
+checksum = "38ddc9bd6c28aeb303477170ddd183760a956a03e083b3902a990238a7e3792d"
 dependencies = [
  "base64-simd",
  "bytes",
  "bytes-utils",
  "futures-core",
  "http 0.2.12",
- "http 1.1.0",
+ "http 1.2.0",
  "http-body 0.4.6",
  "http-body 1.0.1",
  "http-body-util",
@@ -739,10 +731,10 @@ dependencies = [
  "base64 0.22.1",
  "bytes",
  "futures-util",
- "http 1.1.0",
+ "http 1.2.0",
  "http-body 1.0.1",
  "http-body-util",
- "hyper 1.5.1",
+ "hyper 1.5.2",
  "hyper-util",
  "itoa",
  "matchit",
@@ -775,7 +767,7 @@ dependencies = [
  "async-trait",
  "bytes",
  "futures-util",
- "http 1.1.0",
+ "http 1.2.0",
  "http-body 1.0.1",
  "http-body-util",
  "mime",
@@ -795,7 +787,7 @@ checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -827,17 +819,17 @@ dependencies = [
  "fitness-resolver",
  "fitness-service",
  "futures",
- "http 1.1.0",
+ "http 1.2.0",
  "importer-resolver",
  "importer-service",
  "integration-service",
- "itertools 0.13.0",
+ "itertools 0.14.0",
  "logs-wheel",
  "migrations",
  "miscellaneous-resolver",
  "miscellaneous-service",
  "openidconnect",
- "reqwest 0.12.9",
+ "reqwest 0.12.12",
  "router-resolver",
  "schematic",
  "sea-orm",
@@ -940,9 +932,9 @@ dependencies = [
 
 [[package]]
 name = "bigdecimal"
-version = "0.4.6"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f850665a0385e070b64c38d2354e6c104c8479c59868d1e48a0c13ee2c7a1c1"
+checksum = "7f31f3af01c5c65a07985c804d3366560e6fa7883d640a122819b14ec327482c"
 dependencies = [
  "autocfg",
  "libm",
@@ -1052,7 +1044,7 @@ dependencies = [
  "proc-macro-crate",
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -1141,12 +1133,12 @@ dependencies = [
  "database-models",
  "dependent-models",
  "env-utils",
+ "itertools 0.14.0",
  "sea-orm",
  "sea-query",
  "serde",
  "serde_json",
  "tracing",
- "uuid",
 ]
 
 [[package]]
@@ -1160,9 +1152,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.2.2"
+version = "1.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc"
+checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e"
 dependencies = [
  "shlex",
 ]
@@ -1181,9 +1173,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
 
 [[package]]
 name = "chrono"
-version = "0.4.38"
+version = "0.4.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
+checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
@@ -1227,9 +1219,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.21"
+version = "4.5.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f"
+checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -1237,9 +1229,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.21"
+version = "4.5.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec"
+checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
 dependencies = [
  "anstream",
  "anstyle",
@@ -1256,14 +1248,14 @@ dependencies = [
  "heck 0.5.0",
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
 name = "clap_lex"
-version = "0.7.3"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7"
+checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
 
 [[package]]
 name = "collection-resolver"
@@ -1305,15 +1297,12 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
 
 [[package]]
 name = "combine"
-version = "3.8.1"
+version = "4.6.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
 dependencies = [
- "ascii",
- "byteorder",
- "either",
+ "bytes",
  "memchr",
- "unreachable",
 ]
 
 [[package]]
@@ -1330,7 +1319,7 @@ dependencies = [
  "sea-orm",
  "serde",
  "serde_json",
- "serde_with 3.11.0",
+ "serde_with 3.12.0",
  "strum",
 ]
 
@@ -1343,20 +1332,21 @@ dependencies = [
  "const-str",
  "enum-models",
  "env-utils",
- "reqwest 0.12.9",
+ "reqwest 0.12.12",
  "serde",
  "tokio",
 ]
 
 [[package]]
 name = "compact_str"
-version = "0.7.1"
+version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f"
+checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32"
 dependencies = [
  "castaway",
  "cfg-if",
  "itoa",
+ "rustversion",
  "ryu",
  "static_assertions",
 ]
@@ -1417,15 +1407,6 @@ dependencies = [
  "unicode-segmentation",
 ]
 
-[[package]]
-name = "copy_dir"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "543d1dd138ef086e2ff05e3a48cf9da045da2033d16f8538fd76b86cd49b2ca3"
-dependencies = [
- "walkdir",
-]
-
 [[package]]
 name = "core-foundation"
 version = "0.9.4"
@@ -1497,18 +1478,18 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-queue"
-version = "0.3.11"
+version = "0.3.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
+checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
 dependencies = [
  "crossbeam-utils",
 ]
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.20"
+version = "0.8.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
 
 [[package]]
 name = "crypto-bigint"
@@ -1573,7 +1554,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
 dependencies = [
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -1621,7 +1602,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -1645,7 +1626,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "strsim",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -1656,7 +1637,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
 dependencies = [
  "darling_core",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -1689,7 +1670,7 @@ dependencies = [
  "sea-orm",
  "serde",
  "serde_json",
- "serde_with 3.11.0",
+ "serde_with 3.12.0",
  "user-models",
  "uuid",
 ]
@@ -1709,7 +1690,7 @@ dependencies = [
  "enum-models",
  "fitness-models",
  "futures",
- "itertools 0.13.0",
+ "itertools 0.14.0",
  "jwt-service",
  "markdown",
  "media-models",
@@ -1742,7 +1723,7 @@ dependencies = [
  "sea-orm",
  "serde",
  "serde_json",
- "serde_with 3.11.0",
+ "serde_with 3.12.0",
  "strum",
 ]
 
@@ -1764,7 +1745,7 @@ dependencies = [
  "file-storage-service",
  "fitness-models",
  "importer-models",
- "itertools 0.13.0",
+ "itertools 0.14.0",
  "media-models",
  "migrations",
  "nanoid",
@@ -1823,7 +1804,7 @@ checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -1843,7 +1824,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -1925,7 +1906,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -2031,14 +2012,14 @@ dependencies = [
  "enum-ordinalize",
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
 name = "ego-tree"
-version = "0.9.0"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c6ba7d4eec39eaa9ab24d44a0e73a7949a1095a8b3f3abb11eddf27dbb56a53"
+checksum = "b2972feb8dffe7bc8c5463b1dacda1b0dfbed3710e50f977d965429692d74cd8"
 
 [[package]]
 name = "either"
@@ -2151,7 +2132,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -2241,11 +2222,11 @@ dependencies = [
  "dependent-utils",
  "enum-models",
  "fitness-models",
- "itertools 0.13.0",
+ "itertools 0.14.0",
  "media-models",
  "mime_guess",
  "nanoid",
- "reqwest 0.12.9",
+ "reqwest 0.12.12",
  "sea-orm",
  "struson",
  "supporting-service",
@@ -2254,16 +2235,32 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "external-models"
+version = "0.1.0"
+dependencies = [
+ "common-models",
+ "rust_decimal",
+ "sea-orm",
+ "serde",
+ "serde_with 3.12.0",
+ "strum",
+]
+
 [[package]]
 name = "external-utils"
 version = "0.1.0"
 dependencies = [
  "anyhow",
  "common-utils",
- "reqwest 0.12.9",
+ "database-models",
+ "dependent-utils",
+ "enum-models",
+ "reqwest 0.12.12",
  "sea-orm",
  "serde",
  "serde_json",
+ "supporting-service",
  "tracing",
 ]
 
@@ -2289,9 +2286,9 @@ dependencies = [
 
 [[package]]
 name = "fastrand"
-version = "2.2.0"
+version = "2.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
 
 [[package]]
 name = "ff"
@@ -2352,7 +2349,7 @@ dependencies = [
  "sea-orm",
  "serde",
  "serde_json",
- "serde_with 3.11.0",
+ "serde_with 3.12.0",
 ]
 
 [[package]]
@@ -2387,7 +2384,7 @@ dependencies = [
  "fitness-models",
  "futures",
  "nanoid",
- "reqwest 0.12.9",
+ "reqwest 0.12.12",
  "sea-orm",
  "sea-query",
  "supporting-service",
@@ -2423,9 +2420,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 [[package]]
 name = "foldhash"
-version = "0.1.3"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
+checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
 
 [[package]]
 name = "foreign-types"
@@ -2534,7 +2531,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -2584,27 +2581,28 @@ dependencies = [
 
 [[package]]
 name = "garde"
-version = "0.20.0"
+version = "0.21.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a3233677ea1554a48235d81bb59d2a41654969a8e29a1316c48105fd1701693"
+checksum = "f4bd1d7843e437a4caf1d6a9112ba1ee9635b09d909af22aa4e6ec01fe971e22"
 dependencies = [
  "compact_str",
  "garde_derive",
  "once_cell",
  "regex",
+ "rust_decimal",
  "smallvec",
 ]
 
 [[package]]
 name = "garde_derive"
-version = "0.20.0"
+version = "0.21.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8796f322e43105351a7ec35148807b32b5b6058a539656dafe4a5b456d5ca41f"
+checksum = "a0636cbdc03994db48fc89a0ce7765bd68d08bd8a7a68cb9a36bcde96790f413"
 dependencies = [
  "proc-macro2",
  "quote",
  "regex",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -2663,9 +2661,9 @@ dependencies = [
 
 [[package]]
 name = "graphql-parser"
-version = "0.4.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2ebc8013b4426d5b81a4364c419a95ed0b404af2b82e2457de52d9348f0e474"
+checksum = "7a818c0d883d7c0801df27be910917750932be279c7bc82dc541b8769425f409"
 dependencies = [
  "combine",
  "thiserror 1.0.69",
@@ -2762,7 +2760,7 @@ dependencies = [
  "fnv",
  "futures-core",
  "futures-sink",
- "http 1.1.0",
+ "http 1.2.0",
  "indexmap 2.7.0",
  "slab",
  "tokio",
@@ -2873,11 +2871,11 @@ dependencies = [
 
 [[package]]
 name = "home"
-version = "0.5.9"
+version = "0.5.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
+checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
 dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -2902,7 +2900,7 @@ dependencies = [
  "markup5ever",
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -2918,9 +2916,9 @@ dependencies = [
 
 [[package]]
 name = "http"
-version = "1.1.0"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
+checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
 dependencies = [
  "bytes",
  "fnv",
@@ -2945,7 +2943,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
 dependencies = [
  "bytes",
- "http 1.1.0",
+ "http 1.2.0",
 ]
 
 [[package]]
@@ -2956,7 +2954,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
 dependencies = [
  "bytes",
  "futures-util",
- "http 1.1.0",
+ "http 1.2.0",
  "http-body 1.0.1",
  "pin-project-lite",
 ]
@@ -2984,18 +2982,18 @@ dependencies = [
 
 [[package]]
 name = "hybrid-array"
-version = "0.2.1"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45a9a965bb102c1c891fb017c09a05c965186b1265a207640f323ddd009f9deb"
+checksum = "f2d35805454dc9f8662a98d6d61886ffe26bd465f5960e0e55345c70d5c0d2a9"
 dependencies = [
  "typenum",
 ]
 
 [[package]]
 name = "hyper"
-version = "0.14.31"
+version = "0.14.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85"
+checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7"
 dependencies = [
  "bytes",
  "futures-channel",
@@ -3017,15 +3015,15 @@ dependencies = [
 
 [[package]]
 name = "hyper"
-version = "1.5.1"
+version = "1.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f"
+checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0"
 dependencies = [
  "bytes",
  "futures-channel",
  "futures-util",
  "h2 0.4.7",
- "http 1.1.0",
+ "http 1.2.0",
  "http-body 1.0.1",
  "httparse",
  "httpdate",
@@ -3044,7 +3042,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
 dependencies = [
  "futures-util",
  "http 0.2.12",
- "hyper 0.14.31",
+ "hyper 0.14.32",
  "log",
  "rustls 0.21.12",
  "rustls-native-certs",
@@ -3054,18 +3052,18 @@ dependencies = [
 
 [[package]]
 name = "hyper-rustls"
-version = "0.27.3"
+version = "0.27.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
+checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
 dependencies = [
  "futures-util",
- "http 1.1.0",
- "hyper 1.5.1",
+ "http 1.2.0",
+ "hyper 1.5.2",
  "hyper-util",
- "rustls 0.23.19",
+ "rustls 0.23.20",
  "rustls-pki-types",
  "tokio",
- "tokio-rustls 0.26.0",
+ "tokio-rustls 0.26.1",
  "tower-service",
 ]
 
@@ -3076,7 +3074,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
 dependencies = [
  "bytes",
- "hyper 0.14.31",
+ "hyper 0.14.32",
  "native-tls",
  "tokio",
  "tokio-native-tls",
@@ -3090,7 +3088,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
 dependencies = [
  "bytes",
  "http-body-util",
- "hyper 1.5.1",
+ "hyper 1.5.2",
  "hyper-util",
  "native-tls",
  "tokio",
@@ -3107,9 +3105,9 @@ dependencies = [
  "bytes",
  "futures-channel",
  "futures-util",
- "http 1.1.0",
+ "http 1.2.0",
  "http-body 1.0.1",
- "hyper 1.5.1",
+ "hyper 1.5.2",
  "pin-project-lite",
  "socket2",
  "tokio",
@@ -3255,7 +3253,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -3328,25 +3326,24 @@ dependencies = [
  "enum-models",
  "enum_meta",
  "env-utils",
+ "external-models",
  "external-utils",
  "fitness-models",
  "flate2",
  "importer-models",
  "indexmap 2.7.0",
- "itertools 0.13.0",
+ "itertools 0.14.0",
  "media-models",
  "nest_struct",
  "providers",
- "reqwest 0.12.9",
+ "reqwest 0.12.12",
  "rust_decimal",
  "rust_decimal_macros",
  "sea-orm",
  "serde",
  "serde-xml-rs",
  "serde_json",
- "serde_with 3.11.0",
- "specific-models",
- "specific-utils",
+ "serde_with 3.12.0",
  "supporting-service",
  "tracing",
  "traits",
@@ -3388,7 +3385,7 @@ checksum = "0122b7114117e64a63ac49f752a5ca4624d534c7b1c7de796ac196381cd2d947"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -3407,22 +3404,22 @@ dependencies = [
  "dependent-utils",
  "enum-models",
  "eventsource-stream",
+ "external-models",
  "external-utils",
- "itertools 0.13.0",
+ "itertools 0.14.0",
  "media-models",
  "providers",
  "radarr-api-rs",
  "regex",
- "reqwest 0.12.9",
+ "reqwest 0.12.12",
  "rust_decimal",
  "rust_decimal_macros",
+ "rustypipe",
  "sea-orm",
  "sea-query",
  "serde",
  "serde_json",
  "sonarr-api-rs",
- "specific-models",
- "specific-utils",
  "supporting-service",
  "tokio",
  "tracing",
@@ -3488,9 +3485,9 @@ dependencies = [
 
 [[package]]
 name = "itertools"
-version = "0.13.0"
+version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
 dependencies = [
  "either",
 ]
@@ -3503,10 +3500,11 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
 
 [[package]]
 name = "js-sys"
-version = "0.3.73"
+version = "0.3.76"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb15147158e79fd8b8afd0252522769c4f48725460b37338544d8379d94fc8f9"
+checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
 dependencies = [
+ "once_cell",
  "wasm-bindgen",
 ]
 
@@ -3531,7 +3529,7 @@ dependencies = [
  "chrono",
  "jsonwebtoken",
  "serde",
- "serde_with 3.11.0",
+ "serde_with 3.12.0",
  "uuid",
 ]
 
@@ -3546,9 +3544,9 @@ dependencies = [
 
 [[package]]
 name = "lettre"
-version = "0.11.10"
+version = "0.11.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0161e452348e399deb685ba05e55ee116cae9410f4f51fe42d597361444521d9"
+checksum = "ab4c9a167ff73df98a5ecc07e8bf5ce90b583665da3d1762eb1f775ad4d0d6f5"
 dependencies = [
  "base64 0.22.1",
  "chumsky",
@@ -3571,9 +3569,9 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.167"
+version = "0.2.169"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
 
 [[package]]
 name = "libm"
@@ -3581,16 +3579,6 @@ version = "0.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
 
-[[package]]
-name = "libquickjs-dtp-sys"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b869d7bd9cfc01d9ee1b29a7556990897268da33765d750be3f51379e850fb95"
-dependencies = [
- "cc",
- "copy_dir",
-]
-
 [[package]]
 name = "libredox"
 version = "0.1.3"
@@ -3728,7 +3716,7 @@ dependencies = [
  "sea-orm",
  "serde",
  "serde_json",
- "serde_with 3.11.0",
+ "serde_with 3.12.0",
 ]
 
 [[package]]
@@ -3757,7 +3745,7 @@ checksum = "23c9b935fbe1d6cbd1dac857b54a688145e2d93f48db36010514d0f612d0ad67"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -3795,9 +3783,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
 [[package]]
 name = "miniz_oxide"
-version = "0.8.0"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
+checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
 dependencies = [
  "adler2",
 ]
@@ -3841,7 +3829,7 @@ dependencies = [
  "dependent-utils",
  "enum-models",
  "futures",
- "itertools 0.13.0",
+ "itertools 0.14.0",
  "markdown",
  "media-models",
  "migrations",
@@ -3870,7 +3858,7 @@ dependencies = [
  "bytes",
  "encoding_rs",
  "futures-util",
- "http 1.1.0",
+ "http 1.2.0",
  "httparse",
  "memchr",
  "mime",
@@ -3913,7 +3901,7 @@ dependencies = [
  "convert_case",
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -3942,7 +3930,7 @@ dependencies = [
  "config",
  "convert_case",
  "lettre",
- "reqwest 0.12.9",
+ "reqwest 0.12.12",
  "serde",
  "serde_json",
  "tracing",
@@ -4044,9 +4032,9 @@ dependencies = [
 
 [[package]]
 name = "object"
-version = "0.36.5"
+version = "0.36.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
+checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
 dependencies = [
  "memchr",
 ]
@@ -4082,7 +4070,7 @@ dependencies = [
  "serde_json",
  "serde_path_to_error",
  "serde_plain",
- "serde_with 3.11.0",
+ "serde_with 3.12.0",
  "sha2",
  "subtle",
  "thiserror 1.0.69",
@@ -4112,7 +4100,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -4179,7 +4167,7 @@ dependencies = [
  "proc-macro2",
  "proc-macro2-diagnostics",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -4313,20 +4301,20 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
 
 [[package]]
 name = "pest"
-version = "2.7.14"
+version = "2.7.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442"
+checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
 dependencies = [
  "memchr",
- "thiserror 1.0.69",
+ "thiserror 2.0.9",
  "ucd-trie",
 ]
 
 [[package]]
 name = "pest_derive"
-version = "2.7.14"
+version = "2.7.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd"
+checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e"
 dependencies = [
  "pest",
  "pest_generator",
@@ -4334,22 +4322,22 @@ dependencies = [
 
 [[package]]
 name = "pest_generator"
-version = "2.7.14"
+version = "2.7.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e"
+checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b"
 dependencies = [
  "pest",
  "pest_meta",
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
 name = "pest_meta"
-version = "2.7.14"
+version = "2.7.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d"
+checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea"
 dependencies = [
  "once_cell",
  "pest",
@@ -4406,7 +4394,7 @@ dependencies = [
  "phf_shared 0.11.2",
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -4444,7 +4432,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -4568,7 +4556,7 @@ dependencies = [
  "proc-macro-error-attr2",
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -4588,7 +4576,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
  "version_check",
  "yansi",
 ]
@@ -4612,11 +4600,11 @@ dependencies = [
  "enum-models",
  "graphql_client",
  "hashbag",
- "itertools 0.13.0",
+ "itertools 0.14.0",
  "media-models",
  "paginate",
  "rand 0.9.0-beta.0",
- "reqwest 0.12.9",
+ "reqwest 0.12.12",
  "rust_decimal",
  "rust_decimal_macros",
  "rust_iso3166",
@@ -4625,7 +4613,7 @@ dependencies = [
  "sea-orm",
  "serde",
  "serde_json",
- "serde_with 3.11.0",
+ "serde_with 3.12.0",
  "strum",
  "supporting-service",
  "tracing",
@@ -4661,21 +4649,11 @@ dependencies = [
  "syn 1.0.109",
 ]
 
-[[package]]
-name = "quick-js-dtp"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29d07297b897136123d4bb9c63249b587aafbd1a74dcc7f86848c41e506655ab"
-dependencies = [
- "libquickjs-dtp-sys",
- "once_cell",
-]
-
 [[package]]
 name = "quote"
-version = "1.0.37"
+version = "1.0.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
 dependencies = [
  "proc-macro2",
 ]
@@ -4726,7 +4704,7 @@ checksum = "64535f75665ca4111b59c1b2c9eb99358dc57c3775cc3379675c11dca030f81a"
 dependencies = [
  "rand_chacha 0.9.0-beta.0",
  "rand_core 0.9.0-beta.0",
- "zerocopy 0.8.11",
+ "zerocopy 0.8.14",
 ]
 
 [[package]]
@@ -4765,14 +4743,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "faff9cb0513c709c88c66ba1c44e7dfb0e6343494c57576614524400ee60d7cb"
 dependencies = [
  "getrandom",
- "zerocopy 0.8.11",
+ "zerocopy 0.8.14",
 ]
 
 [[package]]
 name = "redox_syscall"
-version = "0.5.7"
+version = "0.5.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
+checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
 dependencies = [
  "bitflags 2.6.0",
 ]
@@ -4861,7 +4839,7 @@ dependencies = [
  "h2 0.3.26",
  "http 0.2.12",
  "http-body 0.4.6",
- "hyper 0.14.31",
+ "hyper 0.14.32",
  "hyper-rustls 0.24.2",
  "hyper-tls 0.5.0",
  "ipnet",
@@ -4894,9 +4872,9 @@ dependencies = [
 
 [[package]]
 name = "reqwest"
-version = "0.12.9"
+version = "0.12.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f"
+checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da"
 dependencies = [
  "async-compression",
  "base64 0.22.1",
@@ -4906,11 +4884,11 @@ dependencies = [
  "futures-core",
  "futures-util",
  "h2 0.4.7",
- "http 1.1.0",
+ "http 1.2.0",
  "http-body 1.0.1",
  "http-body-util",
- "hyper 1.5.1",
- "hyper-rustls 0.27.3",
+ "hyper 1.5.2",
+ "hyper-rustls 0.27.5",
  "hyper-tls 0.6.0",
  "hyper-util",
  "ipnet",
@@ -4931,6 +4909,7 @@ dependencies = [
  "tokio",
  "tokio-native-tls",
  "tokio-util",
+ "tower",
  "tower-service",
  "url",
  "wasm-bindgen",
@@ -5030,6 +5009,33 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "rquickjs"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d16661bff09e9ed8e01094a188b463de45ec0693ade55b92ed54027d7ba7c40c"
+dependencies = [
+ "rquickjs-core",
+]
+
+[[package]]
+name = "rquickjs-core"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8db6379e204ef84c0811e90e7cc3e3e4d7688701db68a00d14a6db6849087b"
+dependencies = [
+ "rquickjs-sys",
+]
+
+[[package]]
+name = "rquickjs-sys"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bc352c6b663604c3c186c000cfcc6c271f4b50bc135a285dd6d4f2a42f9790a"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 name = "rsa"
 version = "0.9.7"
@@ -5105,15 +5111,15 @@ dependencies = [
 
 [[package]]
 name = "rustix"
-version = "0.38.41"
+version = "0.38.42"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6"
+checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
 dependencies = [
  "bitflags 2.6.0",
  "errno",
  "libc",
  "linux-raw-sys",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -5130,9 +5136,9 @@ dependencies = [
 
 [[package]]
 name = "rustls"
-version = "0.23.19"
+version = "0.23.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1"
+checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b"
 dependencies = [
  "once_cell",
  "rustls-pki-types",
@@ -5173,9 +5179,9 @@ dependencies = [
 
 [[package]]
 name = "rustls-pki-types"
-version = "1.10.0"
+version = "1.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
+checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37"
 
 [[package]]
 name = "rustls-webpki"
@@ -5200,30 +5206,31 @@ dependencies = [
 
 [[package]]
 name = "rustversion"
-version = "1.0.18"
+version = "1.0.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
+checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
 
 [[package]]
 name = "rustypipe"
-version = "0.7.2"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97dba9b1e6f02758c5dff06157082cdd55e8c6c96ef9bac3bea0b191cc864da7"
+checksum = "4281f0dfe516d0e4165fd31f229eb2f1f06ab79a3bb700f0782300a0870518a5"
 dependencies = [
- "base64 0.22.1",
+ "data-encoding",
  "fancy-regex",
  "once_cell",
  "phf",
- "quick-js-dtp",
  "rand 0.8.5",
  "regex",
- "reqwest 0.12.9",
+ "reqwest 0.12.12",
  "ress",
+ "rquickjs",
  "serde",
  "serde_json",
  "serde_plain",
- "serde_with 3.11.0",
- "thiserror 2.0.3",
+ "serde_with 3.12.0",
+ "sha1",
+ "thiserror 2.0.9",
  "time",
  "tokio",
  "tracing",
@@ -5237,15 +5244,6 @@ version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
 
-[[package]]
-name = "same-file"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
-dependencies = [
- "winapi-util",
-]
-
 [[package]]
 name = "schannel"
 version = "0.1.27"
@@ -5257,14 +5255,14 @@ dependencies = [
 
 [[package]]
 name = "schematic"
-version = "0.17.7"
+version = "0.17.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ef9a634be1649ec5f5516e39eb78d2e84fcf5eef3792acae353d8ade6a7b391"
+checksum = "bc902ccf410e32f7d1fdcad8080729def0cd5e696fb70ca7468bfcd02ff4fe80"
 dependencies = [
  "garde",
  "indexmap 2.7.0",
  "miette",
- "reqwest 0.12.9",
+ "reqwest 0.12.12",
  "schematic_macros",
  "schematic_types",
  "serde",
@@ -5272,22 +5270,22 @@ dependencies = [
  "serde_path_to_error",
  "serde_yaml",
  "starbase_styles",
- "thiserror 2.0.3",
+ "thiserror 2.0.9",
  "toml",
  "tracing",
 ]
 
 [[package]]
 name = "schematic_macros"
-version = "0.17.5"
+version = "0.17.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26d2d4005b3b6482801d60ba093c550d25382401e0c4d1c3fbba80eb07e28626"
+checksum = "55f638e083cb574cc60a6f15a9eecc45bb4ace4bbbeda0e904298788f04e9453"
 dependencies = [
  "convert_case",
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -5312,11 +5310,10 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
 [[package]]
 name = "scraper"
-version = "0.21.0"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0e749d29b2064585327af5038a5a8eb73aeebad4a3472e83531a436563f7208"
+checksum = "cc3d051b884f40e309de6c149734eab57aa8cc1347992710dc80bcc1c2194c15"
 dependencies = [
- "ahash 0.8.11",
  "cssparser",
  "ego-tree",
  "getopts",
@@ -5346,14 +5343,14 @@ dependencies = [
  "proc-macro-error2",
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
 name = "sea-orm"
-version = "1.1.1"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5680a8b686985116607ef5f5af2b1f9e1cc2c228330e93101816a0baa279afa"
+checksum = "0dbcf83248860dc632c46c7e81a221e041b50d0006191756cb001d9e8afc60a9"
 dependencies = [
  "async-stream",
  "async-trait",
@@ -5379,9 +5376,9 @@ dependencies = [
 
 [[package]]
 name = "sea-orm-cli"
-version = "1.1.1"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70a157f42d291ccbd6e913b9d9b12dbe2ccbcf0472efc60c8715dd1254083aec"
+checksum = "7a8dbef29c7e534a8e9afb49abfa946c7a9df3d85f610dfed9140215ff8bff17"
 dependencies = [
  "chrono",
  "clap",
@@ -5396,23 +5393,23 @@ dependencies = [
 
 [[package]]
 name = "sea-orm-macros"
-version = "1.1.1"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a239e3bb1b566ad4ec2654d0d193d6ceddfd733487edc9c21a64d214c773910"
+checksum = "49ce6f08134f3681b1ca92185b96fac898f26d9b4f5538d13f7032ef243d14b2"
 dependencies = [
  "heck 0.4.1",
  "proc-macro2",
  "quote",
  "sea-bae",
- "syn 2.0.90",
+ "syn 2.0.92",
  "unicode-ident",
 ]
 
 [[package]]
 name = "sea-orm-migration"
-version = "1.1.1"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "63ba07e9f2479cc671758fcb1edee42ff2e32c34b3e67ab41d0af1e41f73c74e"
+checksum = "2e53f46fe9874161ba57b15ff45d2589d754d7cbbbaab9470cb79cbada149ca7"
 dependencies = [
  "async-trait",
  "clap",
@@ -5427,9 +5424,9 @@ dependencies = [
 
 [[package]]
 name = "sea-query"
-version = "0.32.0"
+version = "0.32.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff504d13b5e4b52fffcf2fb203d0352a5722fa5151696db768933e41e1e591bb"
+checksum = "085e94f7d7271c0393ac2d164a39994b1dff1b06bc40cd9a0da04f3d672b0fee"
 dependencies = [
  "chrono",
  "inherent",
@@ -5464,15 +5461,15 @@ dependencies = [
  "heck 0.4.1",
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
  "thiserror 1.0.69",
 ]
 
 [[package]]
 name = "sea-schema"
-version = "0.16.0"
+version = "0.16.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aab1592d17860a9a8584d9b549aebcd06f7bdc3ff615f71752486ba0b05b1e6e"
+checksum = "0ef5dd7848c993f3789d09a2616484c72c9330cae2b048df59d8c9b8c0343e95"
 dependencies = [
  "futures",
  "sea-query",
@@ -5488,7 +5485,7 @@ dependencies = [
  "heck 0.4.1",
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -5540,9 +5537,9 @@ dependencies = [
 
 [[package]]
 name = "security-framework-sys"
-version = "2.12.1"
+version = "2.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2"
+checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5"
 dependencies = [
  "core-foundation-sys",
  "libc",
@@ -5569,15 +5566,15 @@ dependencies = [
 
 [[package]]
 name = "semver"
-version = "1.0.23"
+version = "1.0.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
+checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
 
 [[package]]
 name = "serde"
-version = "1.0.215"
+version = "1.0.217"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
+checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
 dependencies = [
  "serde_derive",
 ]
@@ -5606,20 +5603,20 @@ dependencies = [
 
 [[package]]
 name = "serde_derive"
-version = "1.0.215"
+version = "1.0.217"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
+checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.133"
+version = "1.0.134"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
+checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
 dependencies = [
  "indexmap 2.7.0",
  "itoa",
@@ -5686,9 +5683,9 @@ dependencies = [
 
 [[package]]
 name = "serde_with"
-version = "3.11.0"
+version = "3.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817"
+checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa"
 dependencies = [
  "base64 0.22.1",
  "chrono",
@@ -5698,7 +5695,7 @@ dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
- "serde_with_macros 3.11.0",
+ "serde_with_macros 3.12.0",
  "time",
 ]
 
@@ -5711,19 +5708,19 @@ dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
 name = "serde_with_macros"
-version = "3.11.0"
+version = "3.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d"
+checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e"
 dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -5870,39 +5867,14 @@ version = "3.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a0f85e838c6093957978793ece2c0893c838da8dd5bcab0ff6f57fcaa8fc8ee9"
 dependencies = [
- "reqwest 0.12.9",
+ "reqwest 0.12.12",
  "serde",
  "serde_json",
- "serde_with 3.11.0",
+ "serde_with 3.12.0",
  "url",
  "uuid",
 ]
 
-[[package]]
-name = "specific-models"
-version = "0.1.0"
-dependencies = [
- "common-models",
- "rust_decimal",
- "sea-orm",
- "serde",
- "serde_with 3.11.0",
- "strum",
-]
-
-[[package]]
-name = "specific-utils"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "common-utils",
- "database-models",
- "dependent-utils",
- "enum-models",
- "sea-orm",
- "supporting-service",
-]
-
 [[package]]
 name = "spin"
 version = "0.9.8"
@@ -6008,7 +5980,7 @@ dependencies = [
  "quote",
  "sqlx-core",
  "sqlx-macros-core",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -6031,7 +6003,7 @@ dependencies = [
  "sqlx-mysql",
  "sqlx-postgres",
  "sqlx-sqlite",
- "syn 2.0.90",
+ "syn 2.0.92",
  "tempfile",
  "tokio",
  "url",
@@ -6169,9 +6141,9 @@ dependencies = [
 
 [[package]]
 name = "starbase_styles"
-version = "0.4.6"
+version = "0.4.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1664b29d64bf37a14a0f50ae4c1568eb712c7eb28fb489aabac81c36e5558c1"
+checksum = "1a4df972f8b4010b3ca083555953f6b320de4962aa2b63823c273de9830a3e9f"
 dependencies = [
  "dirs",
  "owo-colors",
@@ -6212,7 +6184,7 @@ dependencies = [
  "dependent-models",
  "enum-models",
  "hashbag",
- "itertools 0.13.0",
+ "itertools 0.14.0",
  "sea-orm",
  "supporting-service",
  "tokio",
@@ -6281,7 +6253,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rustversion",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -6321,7 +6293,7 @@ dependencies = [
  "env-utils",
  "file-storage-service",
  "isolang",
- "itertools 0.13.0",
+ "itertools 0.14.0",
  "openidconnect",
  "rustypipe",
  "sea-orm",
@@ -6353,9 +6325,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.90"
+version = "2.0.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
+checksum = "70ae51629bf965c5c098cc9e87908a3df5301051a9e087d6f9bef5c9771ed126"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -6385,7 +6357,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -6482,11 +6454,11 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "2.0.3"
+version = "2.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa"
+checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
 dependencies = [
- "thiserror-impl 2.0.3",
+ "thiserror-impl 2.0.9",
 ]
 
 [[package]]
@@ -6497,18 +6469,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "2.0.3"
+version = "2.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568"
+checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -6523,9 +6495,9 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.3.36"
+version = "0.3.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
+checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
 dependencies = [
  "deranged",
  "itoa",
@@ -6544,9 +6516,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
 
 [[package]]
 name = "time-macros"
-version = "0.2.18"
+version = "0.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
+checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
 dependencies = [
  "num-conv",
  "time-core",
@@ -6564,9 +6536,9 @@ dependencies = [
 
 [[package]]
 name = "tinyvec"
-version = "1.8.0"
+version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
+checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8"
 dependencies = [
  "tinyvec_macros",
 ]
@@ -6579,9 +6551,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 [[package]]
 name = "tokio"
-version = "1.41.1"
+version = "1.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33"
+checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
 dependencies = [
  "backtrace",
  "bytes",
@@ -6603,7 +6575,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -6628,20 +6600,19 @@ dependencies = [
 
 [[package]]
 name = "tokio-rustls"
-version = "0.26.0"
+version = "0.26.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
+checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
 dependencies = [
- "rustls 0.23.19",
- "rustls-pki-types",
+ "rustls 0.23.20",
  "tokio",
 ]
 
 [[package]]
 name = "tokio-stream"
-version = "0.1.16"
+version = "0.1.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1"
+checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
 dependencies = [
  "futures-core",
  "pin-project-lite",
@@ -6662,9 +6633,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-util"
-version = "0.7.12"
+version = "0.7.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a"
+checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
 dependencies = [
  "bytes",
  "futures-core",
@@ -6710,14 +6681,14 @@ dependencies = [
 
 [[package]]
 name = "tower"
-version = "0.5.1"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f"
+checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
 dependencies = [
  "futures-core",
  "futures-util",
  "pin-project-lite",
- "sync_wrapper 0.1.2",
+ "sync_wrapper 1.0.2",
  "tokio",
  "tokio-util",
  "tower-layer",
@@ -6734,7 +6705,7 @@ dependencies = [
  "bitflags 2.6.0",
  "bytes",
  "futures-util",
- "http 1.1.0",
+ "http 1.2.0",
  "http-body 1.0.1",
  "http-body-util",
  "pin-project-lite",
@@ -6775,7 +6746,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -6863,7 +6834,7 @@ dependencies = [
  "byteorder",
  "bytes",
  "data-encoding",
- "http 1.1.0",
+ "http 1.2.0",
  "httparse",
  "log",
  "rand 0.8.5",
@@ -6897,15 +6868,15 @@ dependencies = [
 
 [[package]]
 name = "unicase"
-version = "2.8.0"
+version = "2.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
+checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
 
 [[package]]
 name = "unicode-bidi"
-version = "0.3.17"
+version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893"
+checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
 
 [[package]]
 name = "unicode-id"
@@ -6970,15 +6941,6 @@ dependencies = [
  "serde_json",
 ]
 
-[[package]]
-name = "unreachable"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
-dependencies = [
- "void",
-]
-
 [[package]]
 name = "unsafe-libyaml"
 version = "0.2.11"
@@ -7021,7 +6983,7 @@ dependencies = [
  "sea-orm",
  "serde",
  "serde_json",
- "serde_with 3.11.0",
+ "serde_with 3.12.0",
  "strum",
 ]
 
@@ -7055,12 +7017,13 @@ dependencies = [
  "dependent-utils",
  "enum-models",
  "enum_meta",
- "itertools 0.13.0",
+ "itertools 0.14.0",
  "jwt-service",
  "media-models",
  "nanoid",
  "notification-service",
  "openidconnect",
+ "rand 0.9.0-beta.0",
  "sea-orm",
  "supporting-service",
  "tracing",
@@ -7119,28 +7082,12 @@ version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
 
-[[package]]
-name = "void"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
-
 [[package]]
 name = "vsimd"
 version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64"
 
-[[package]]
-name = "walkdir"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
-dependencies = [
- "same-file",
- "winapi-util",
-]
-
 [[package]]
 name = "want"
 version = "0.3.1"
@@ -7164,9 +7111,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.96"
+version = "0.2.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21d3b25c3ea1126a2ad5f4f9068483c2af1e64168f847abe863a526b8dbfe00b"
+checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
 dependencies = [
  "cfg-if",
  "once_cell",
@@ -7175,24 +7122,23 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.96"
+version = "0.2.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52857d4c32e496dc6537646b5b117081e71fd2ff06de792e3577a150627db283"
+checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
 dependencies = [
  "bumpalo",
  "log",
- "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-futures"
-version = "0.4.46"
+version = "0.4.49"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "951fe82312ed48443ac78b66fa43eded9999f738f6022e67aead7b708659e49a"
+checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2"
 dependencies = [
  "cfg-if",
  "js-sys",
@@ -7203,9 +7149,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.96"
+version = "0.2.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "920b0ffe069571ebbfc9ddc0b36ba305ef65577c94b06262ed793716a1afd981"
+checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -7213,22 +7159,22 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.96"
+version = "0.2.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf59002391099644be3524e23b781fa43d2be0c5aa0719a18c0731b9d195cab6"
+checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.96"
+version = "0.2.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5047c5392700766601942795a436d7d2599af60dcc3cc1248c9120bfb0827b0"
+checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
 
 [[package]]
 name = "wasm-streams"
@@ -7245,9 +7191,9 @@ dependencies = [
 
 [[package]]
 name = "web-sys"
-version = "0.3.73"
+version = "0.3.76"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "476364ff87d0ae6bfb661053a9104ab312542658c3d8f963b7ace80b6f9b26b9"
+checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -7295,15 +7241,6 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
-[[package]]
-name = "winapi-util"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
-dependencies = [
- "windows-sys 0.59.0",
-]
-
 [[package]]
 name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
@@ -7549,9 +7486,9 @@ dependencies = [
 
 [[package]]
 name = "xml-rs"
-version = "0.8.23"
+version = "0.8.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af310deaae937e48a26602b730250b4949e125f468f11e6990be3e5304ddd96f"
+checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432"
 
 [[package]]
 name = "xmlparser"
@@ -7585,7 +7522,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
  "synstructure",
 ]
 
@@ -7601,11 +7538,11 @@ dependencies = [
 
 [[package]]
 name = "zerocopy"
-version = "0.8.11"
+version = "0.8.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cce3b5629d87654b53a49002acc2ce64aa5aa7255f5c718374a37ac7fd98c218"
+checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468"
 dependencies = [
- "zerocopy-derive 0.8.11",
+ "zerocopy-derive 0.8.14",
 ]
 
 [[package]]
@@ -7616,18 +7553,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
 name = "zerocopy-derive"
-version = "0.8.11"
+version = "0.8.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74a82c26c3986af2623ec9eb890ff4aa19c006e30a1133dc9bd1830ec1612e20"
+checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
 
 [[package]]
@@ -7647,7 +7584,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
  "synstructure",
 ]
 
@@ -7676,5 +7613,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.90",
+ "syn 2.0.92",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index fc18019725..b1c7b9cca7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,10 +8,10 @@ members = [
     "crates/models/database",
     "crates/models/dependent",
     "crates/models/enum",
+    "crates/models/external",
     "crates/models/fitness",
     "crates/models/importer",
     "crates/models/media",
-    "crates/models/specific",
     "crates/models/user",
     "crates/providers",
     "crates/resolvers/collection",
@@ -43,28 +43,28 @@ members = [
     "crates/utils/dependent",
     "crates/utils/external",
     "crates/utils/env",
-    "crates/utils/specific",
 ]
 resolver = "2"
 
 [workspace.dependencies]
-askama = "0.12.1"
-anyhow = "=1.0.93"
+askama = "=0.12.1"
+anyhow = "=1.0.95"
 apalis = { version = "=0.6.2", features = ["catch-panic", "limit", "retry"] }
 apalis-cron = "=0.6.2"
 argon2 = "=0.6.0-pre.1"
-async-graphql = { version = "=7.0.11", features = [
+async-graphql = { version = "=7.0.13", features = [
     "chrono",
     "decimal",
     "log",
     "tracing",
 ] }
-async-graphql-axum = "=7.0.11"
+async-graphql-axum = "=7.0.13"
 async-trait = "=0.1.83"
-aws-sdk-s3 = { version = "=1.63.0", features = ["behavior-version-latest"] }
+aws-sdk-s3 = { version = "=1.67.0", features = ["behavior-version-latest"] }
+# TODO: Update when https://github.com/async-graphql/async-graphql/pull/1653 is merged
 axum = { version = "=0.7.9", features = ["macros", "multipart"] }
 boilermates = "=0.3.0"
-chrono = "=0.4.38"
+chrono = "=0.4.39"
 chrono-tz = "=0.10.0"
 compile-time = "=0.2.0"
 const-str = "=0.5.7"
@@ -89,13 +89,13 @@ flate2 = "=1.0.35"
 futures = "=0.3.31"
 graphql_client = "=0.14.0"
 hashbag = "=0.1.12"
-http = "=1.1.0"
+http = "=1.2.0"
 indexmap = "=2.7.0"
 indoc = "=2.0.5"
 isolang = { version = "=2.4.0", features = ["list_languages"] }
-itertools = "=0.13.0"
+itertools = "=0.14.0"
 jsonwebtoken = { version = "=9.3.0", default-features = false }
-lettre = "=0.11.10"
+lettre = "=0.11.11"
 logs-wheel = "=0.3.1"
 markdown = "=1.0.0-alpha.21"
 mime_guess = "=2.0.5"
@@ -109,8 +109,8 @@ regex = "=1.11.1"
 rust_decimal = "=1.36.0"
 rust_decimal_macros = "=1.36.0"
 rust_iso3166 = "=0.1.13"
-rustypipe = "0.7.2"
-schematic = { version = "=0.17.7", features = [
+rustypipe = "=0.9.0"
+schematic = { version = "=0.17.8", features = [
     "config",
     "env",
     "json",
@@ -124,8 +124,8 @@ schematic = { version = "=0.17.7", features = [
     "validate",
     "yaml",
 ], default-features = false }
-scraper = "=0.21.0"
-sea-orm = { version = "=1.1.1", features = [
+scraper = "=0.22.0"
+sea-orm = { version = "=1.1.3", features = [
     "debug-print",
     "postgres-array",
     "macros",
@@ -136,21 +136,21 @@ sea-orm = { version = "=1.1.1", features = [
     "with-rust_decimal",
     "with-uuid",
 ], default-features = false }
-sea-orm-migration = "=1.1.1"
-sea-query = "=0.32.0"
-serde = { version = "=1.0.215", features = ["derive"] }
-serde_json = "=1.0.133"
-serde_with = { version = "=3.11.0", features = ["chrono_0_4"] }
+sea-orm-migration = "=1.1.3"
+sea-query = "=0.32.1"
+serde = { version = "=1.0.217", features = ["derive"] }
+serde_json = "=1.0.134"
+serde_with = { version = "=3.12.0", features = ["chrono_0_4"] }
 serde-xml-rs = "=0.6.0"
 slug = "=0.1.6"
 sonarr-api-rs = "=3.0.0"
 sqlx = { version = "=0.8.2", default-features = false, features = ["postgres"] }
 strum = { version = "=0.26.3", features = ["derive"] }
 struson = { version = "=0.6.0", features = ["serde"] }
-reqwest = { version = "=0.12.9", features = ["json", "stream"] }
-tokio = { version = "=1.41.1", features = ["full"] }
-tokio-util = { version = "=0.7.12", features = ["codec"] }
-tower = "=0.5.1"
+reqwest = { version = "=0.12.12", features = ["json", "stream"] }
+tokio = { version = "=1.42.0", features = ["full"] }
+tokio-util = { version = "=0.7.13", features = ["codec"] }
+tower = "=0.5.2"
 tower-http = { version = "=0.6.2", features = ["catch-panic", "cors", "trace"] }
 tracing = { version = "=0.1.41", features = ["attributes"] }
 tracing-subscriber = "=0.3.19"
diff --git a/apps/frontend/app/components/common.tsx b/apps/frontend/app/components/common.tsx
index e593750717..d482e8e69b 100644
--- a/apps/frontend/app/components/common.tsx
+++ b/apps/frontend/app/components/common.tsx
@@ -20,6 +20,7 @@ import {
 	Modal,
 	MultiSelect,
 	Paper,
+	RingProgress,
 	SimpleGrid,
 	Skeleton,
 	Stack,
@@ -28,6 +29,7 @@ import {
 	Title,
 	Tooltip,
 	rem,
+	useMantineTheme,
 } from "@mantine/core";
 import { useDebouncedValue, useDidUpdate, useDisclosure } from "@mantine/hooks";
 import {
@@ -40,29 +42,36 @@ import {
 import {
 	EntityLot,
 	GridPacking,
-	type MediaLot,
+	MediaLot,
 	type MediaSource,
 	type ReviewItem,
+	type UserAnalytics,
 	UserReviewScale,
 } from "@ryot/generated/graphql/backend/graphql";
 import {
 	changeCase,
+	formatQuantityWithCompactNotation,
 	getInitials,
+	humanizeDuration,
 	isNumber,
 	isString,
 	snakeCase,
 } from "@ryot/ts-utils";
 import {
 	IconArrowBigUp,
+	IconBarbell,
 	IconCheck,
 	IconEdit,
 	IconExternalLink,
 	IconFilterOff,
+	IconFriends,
 	IconMoodEmpty,
 	IconMoodHappy,
 	IconMoodSad,
 	IconRefresh,
+	IconScaleOutline,
 	IconSearch,
+	IconServer,
 	IconStarFilled,
 	IconTrash,
 	IconX,
@@ -71,16 +80,18 @@ import { useQuery } from "@tanstack/react-query";
 import clsx from "clsx";
 import Cookies from "js-cookie";
 import type { ReactNode, Ref } from "react";
-import { useState } from "react";
+import { Fragment, useState } from "react";
 import { $path } from "remix-routes";
 import type { DeepPartial } from "ts-essentials";
 import { match } from "ts-pattern";
 import { withQuery } from "ufo";
 import {
+	MediaColors,
 	PRO_REQUIRED_MESSAGE,
 	ThreePointSmileyRating,
 	convertDecimalToThreePointSmiley,
 	dayjsLib,
+	getMetadataIcon,
 	getSurroundingElements,
 	openConfirmationModal,
 	reviewYellow,
@@ -90,10 +101,12 @@ import {
 	useConfirmSubmit,
 	useCoreDetails,
 	useFallbackImageUrl,
+	useGetMantineColors,
 	useGetRandomMantineColor,
 	useUserCollections,
 	useUserDetails,
 	useUserPreferences,
+	useUserUnitSystem,
 } from "~/lib/hooks";
 import { useReviewEntity } from "~/lib/state/media";
 import type { action } from "~/routes/actions";
@@ -102,6 +115,7 @@ import {
 	ExerciseDisplayItem,
 	WorkoutDisplayItem,
 	WorkoutTemplateDisplayItem,
+	displayWeightWithUnit,
 } from "./fitness";
 import {
 	MetadataDisplayItem,
@@ -327,7 +341,11 @@ export const BaseMediaDisplayItem = (props: {
 		<Flex justify="space-between" direction="column" ref={props.innerRef}>
 			<Box pos="relative" w="100%">
 				<SurroundingElement>
-					<Tooltip label={props.name} position="top">
+					<Tooltip
+						position="top"
+						label={props.name}
+						disabled={(props.name?.length || 0) === 0}
+					>
 						<Paper
 							radius="md"
 							pos="relative"
@@ -939,3 +957,362 @@ export const DisplayCollection = (props: {
 		</Badge>
 	);
 };
+
+export const DisplaySummarySection = ({
+	latestUserSummary,
+}: {
+	latestUserSummary: UserAnalytics["activities"]["items"][0];
+}) => {
+	const userPreferences = useUserPreferences();
+	const unitSystem = useUserUnitSystem();
+	const theme = useMantineTheme();
+
+	return (
+		<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.Music}
+				data={[
+					{
+						label: "Songs",
+						value: latestUserSummary.musicCount,
+						type: "number",
+					},
+					{
+						label: "Runtime",
+						value: latestUserSummary.totalMusicDuration,
+						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={<IconScaleOutline stroke={1.3} />}
+					lot="Fitness"
+					color={theme.colors.yellow[5]}
+					data={[
+						{
+							label: "Measurements",
+							value: latestUserSummary.userMeasurementCount,
+							type: "number",
+							hideIfZero: true,
+						},
+					]}
+				/>
+			) : null}
+		</SimpleGrid>
+	);
+};
+
+const ActualDisplayStat = (props: {
+	icon: ReactNode;
+	lot: string;
+	data: Array<{
+		type: "duration" | "number" | "string";
+		label: string;
+		value: number | string;
+		hideIfZero?: true;
+	}>;
+	color?: string;
+}) => {
+	const colors = useGetMantineColors();
+
+	return (
+		<Flex align="center">
+			<RingProgress
+				size={60}
+				thickness={4}
+				sections={[]}
+				label={<Center>{props.icon}</Center>}
+				rootColor={props.color ?? colors[11]}
+			/>
+			<Flex wrap="wrap" ml="xs">
+				{props.data.map((d, idx) => (
+					<Fragment key={idx.toString()}>
+						{isNumber(d.type) && d.value === 0 && d.hideIfZero ? undefined : (
+							<Box mx="xs" data-stat-stringified={JSON.stringify(d)}>
+								<Text
+									fw={d.label !== "Runtime" ? "bold" : undefined}
+									display="inline"
+									fz={{ base: "md", md: "sm", xl: "md" }}
+								>
+									{match(d.type)
+										.with("string", () => d.value)
+										.with("duration", () =>
+											humanizeDuration(
+												dayjsLib
+													.duration(Number(d.value), "minutes")
+													.asMilliseconds(),
+												{
+													round: true,
+													largest: 3,
+												},
+											),
+										)
+										.with("number", () =>
+											formatQuantityWithCompactNotation(Number(d.value)),
+										)
+										.exhaustive()}
+								</Text>
+								<Text
+									display="inline"
+									ml="4px"
+									fz={{ base: "md", md: "sm", xl: "md" }}
+								>
+									{d.label === "Runtime" ? "" : d.label}
+								</Text>
+							</Box>
+						)}
+					</Fragment>
+				))}
+			</Flex>
+		</Flex>
+	);
+};
+
+const DisplayStatForMediaType = (props: {
+	lot: MediaLot;
+	data: Array<{
+		type: "duration" | "number";
+		label: string;
+		value: number;
+		hideIfZero?: true;
+	}>;
+}) => {
+	const userPreferences = useUserPreferences();
+	const isEnabled = userPreferences.featuresEnabled.media.specific.includes(
+		props.lot,
+	);
+	const Icon = getMetadataIcon(props.lot);
+	const icon = <Icon size={24} stroke={1.5} />;
+
+	return isEnabled && userPreferences.featuresEnabled.media.enabled ? (
+		<UnstyledLink
+			to={$path("/media/:action/:lot", {
+				action: "list",
+				lot: props.lot.toLowerCase(),
+			})}
+		>
+			<ActualDisplayStat
+				data={props.data}
+				icon={icon}
+				lot={props.lot.toString()}
+				color={MediaColors[props.lot]}
+			/>
+		</UnstyledLink>
+	) : null;
+};
+
+const UnstyledLink = (props: { children: ReactNode; to: string }) => {
+	return (
+		<Link to={props.to} style={{ all: "unset", cursor: "pointer" }}>
+			{props.children}
+		</Link>
+	);
+};
diff --git a/apps/frontend/app/routes/_dashboard._index.tsx b/apps/frontend/app/routes/_dashboard._index.tsx
index eee0c80d55..f84c433b9e 100644
--- a/apps/frontend/app/routes/_dashboard._index.tsx
+++ b/apps/frontend/app/routes/_dashboard._index.tsx
@@ -1,23 +1,17 @@
 import {
 	ActionIcon,
 	Alert,
-	Box,
-	Center,
 	Container,
-	Flex,
 	Group,
-	RingProgress,
-	SimpleGrid,
 	Stack,
 	Text,
-	useMantineTheme,
 } from "@mantine/core";
 import type {
 	ActionFunctionArgs,
 	LoaderFunctionArgs,
 	MetaArgs,
 } from "@remix-run/node";
-import { Form, Link, useLoaderData } from "@remix-run/react";
+import { Form, useLoaderData } from "@remix-run/react";
 import {
 	type CalendarEventPartFragment,
 	CollectionContentsDocument,
@@ -25,51 +19,33 @@ import {
 	DashboardElementLot,
 	GraphqlSortOrder,
 	MediaLot,
-	RefreshUserRecommendationsKeyDocument,
+	RefreshUserMetadataRecommendationsDocument,
 	UserAnalyticsDocument,
+	UserMetadataRecommendationsDocument,
 	type UserPreferences,
-	UserRecommendationsDocument,
 	UserUpcomingCalendarEventsDocument,
 } from "@ryot/generated/graphql/backend/graphql";
-import {
-	formatQuantityWithCompactNotation,
-	getActionIntent,
-	humanizeDuration,
-	isNumber,
-} from "@ryot/ts-utils";
-import {
-	IconBarbell,
-	IconFriends,
-	IconInfoCircle,
-	IconRotateClockwise,
-	IconScaleOutline,
-	IconServer,
-} from "@tabler/icons-react";
+import { getActionIntent, isNumber } from "@ryot/ts-utils";
+import { IconInfoCircle, IconRotateClockwise } from "@tabler/icons-react";
 import CryptoJS from "crypto-js";
-import { Fragment, type ReactNode } from "react";
-import { $path } from "remix-routes";
+import type { ReactNode } from "react";
 import { ClientOnly } from "remix-utils/client-only";
 import invariant from "tiny-invariant";
 import { match } from "ts-pattern";
 import { withQuery } from "ufo";
 import { useLocalStorage } from "usehooks-ts";
-import { ApplicationGrid, ProRequiredAlert } from "~/components/common";
+import {
+	ApplicationGrid,
+	DisplaySummarySection,
+	ProRequiredAlert,
+} from "~/components/common";
 import { DisplayCollectionEntity } from "~/components/common";
-import { displayWeightWithUnit } from "~/components/fitness";
 import { MetadataDisplayItem } from "~/components/media";
-import {
-	MediaColors,
-	dayjsLib,
-	getLot,
-	getMetadataIcon,
-	openConfirmationModal,
-} from "~/lib/generals";
+import { dayjsLib, openConfirmationModal } from "~/lib/generals";
 import {
 	useConfirmSubmit,
 	useCoreDetails,
-	useGetMantineColors,
 	useUserPreferences,
-	useUserUnitSystem,
 } from "~/lib/hooks";
 import {
 	getUserCollectionsList,
@@ -96,12 +72,12 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
 			)?.hidden
 		)
 			return [];
-		const { userRecommendations } = await serverGqlService.authenticatedRequest(
-			request,
-			UserRecommendationsDocument,
-			{},
-		);
-		return userRecommendations;
+		const { userMetadataRecommendations } =
+			await serverGqlService.authenticatedRequest(
+				request,
+				UserMetadataRecommendationsDocument,
+			);
+		return userMetadataRecommendations;
 	};
 	const userCollectionsList = await getUserCollectionsList(request);
 	const foundInProgressCollection = userCollectionsList.find(
@@ -149,11 +125,10 @@ export const meta = (_args: MetaArgs<typeof loader>) => {
 export const action = async ({ request }: ActionFunctionArgs) => {
 	const intent = getActionIntent(request);
 	return await match(intent)
-		.with("refreshUserRecommendationsKey", async () => {
+		.with("refreshUserMetadataRecommendations", async () => {
 			await serverGqlService.authenticatedRequest(
 				request,
-				RefreshUserRecommendationsKeyDocument,
-				{},
+				RefreshUserMetadataRecommendationsDocument,
 			);
 			return {};
 		})
@@ -164,8 +139,6 @@ export default function Page() {
 	const loaderData = useLoaderData<typeof loader>();
 	const coreDetails = useCoreDetails();
 	const userPreferences = useUserPreferences();
-	const unitSystem = useUserUnitSystem();
-	const theme = useMantineTheme();
 	const submit = useConfirmSubmit();
 
 	const dashboardMessage = coreDetails.frontend.dashboardMessage;
@@ -233,7 +206,7 @@ export default function Page() {
 									<Form
 										method="POST"
 										action={withQuery(".?index", {
-											intent: "refreshUserRecommendationsKey",
+											intent: "refreshUserMetadataRecommendations",
 										})}
 									>
 										<ActionIcon
@@ -267,231 +240,9 @@ export default function Page() {
 							latestUserSummary ? (
 								<Section key={v} lot={v}>
 									<SectionTitle text="Summary" />
-									<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={<IconScaleOutline stroke={1.3} />}
-												lot="Fitness"
-												color={theme.colors.yellow[5]}
-												data={[
-													{
-														label: "Measurements",
-														value: latestUserSummary.userMeasurementCount,
-														type: "number",
-														hideIfZero: true,
-													},
-												]}
-											/>
-										) : null}
-									</SimpleGrid>
+									<DisplaySummarySection
+										latestUserSummary={latestUserSummary}
+									/>
 								</Section>
 							) : null,
 						)
@@ -538,105 +289,6 @@ const UpComingMedia = ({ um }: { um: CalendarEventPartFragment }) => {
 	);
 };
 
-const ActualDisplayStat = (props: {
-	icon: ReactNode;
-	lot: string;
-	data: Array<{
-		type: "duration" | "number" | "string";
-		label: string;
-		value: number | string;
-		hideIfZero?: true;
-	}>;
-	color?: string;
-}) => {
-	const colors = useGetMantineColors();
-
-	return (
-		<Flex align="center">
-			<RingProgress
-				size={60}
-				thickness={4}
-				sections={[]}
-				label={<Center>{props.icon}</Center>}
-				rootColor={props.color ?? colors[11]}
-			/>
-			<Flex wrap="wrap" ml="xs">
-				{props.data.map((d, idx) => (
-					<Fragment key={idx.toString()}>
-						{isNumber(d.type) && d.value === 0 && d.hideIfZero ? undefined : (
-							<Box mx="xs" data-stat-stringified={JSON.stringify(d)}>
-								<Text
-									fw={d.label !== "Runtime" ? "bold" : undefined}
-									display="inline"
-									fz={{ base: "md", md: "sm", xl: "md" }}
-								>
-									{match(d.type)
-										.with("string", () => d.value)
-										.with("duration", () =>
-											humanizeDuration(
-												dayjsLib
-													.duration(Number(d.value), "minutes")
-													.asMilliseconds(),
-												{
-													round: true,
-													largest: 3,
-												},
-											),
-										)
-										.with("number", () =>
-											formatQuantityWithCompactNotation(Number(d.value)),
-										)
-										.exhaustive()}
-								</Text>
-								<Text
-									display="inline"
-									ml="4px"
-									fz={{ base: "md", md: "sm", xl: "md" }}
-								>
-									{d.label === "Runtime" ? "" : d.label}
-								</Text>
-							</Box>
-						)}
-					</Fragment>
-				))}
-			</Flex>
-		</Flex>
-	);
-};
-
-const DisplayStatForMediaType = (props: {
-	lot: MediaLot;
-	data: Array<{
-		type: "duration" | "number";
-		label: string;
-		value: number;
-		hideIfZero?: true;
-	}>;
-}) => {
-	const userPreferences = useUserPreferences();
-	const isEnabled = Object.entries(
-		userPreferences.featuresEnabled.media || {},
-	).find(([name, _]) => getLot(name) === props.lot);
-	const Icon = getMetadataIcon(props.lot);
-	const icon = <Icon size={24} stroke={1.5} />;
-
-	return isEnabled?.[1] && userPreferences.featuresEnabled.media.enabled ? (
-		<UnstyledLink
-			to={$path("/media/:action/:lot", {
-				action: "list",
-				lot: props.lot.toLowerCase(),
-			})}
-		>
-			<ActualDisplayStat
-				data={props.data}
-				icon={icon}
-				lot={props.lot.toString()}
-				color={MediaColors[props.lot]}
-			/>
-		</UnstyledLink>
-	) : null;
-};
-
 const Section = (props: {
 	children: ReactNode | Array<ReactNode>;
 	lot: DashboardElementLot;
@@ -647,11 +299,3 @@ const Section = (props: {
 		</Stack>
 	);
 };
-
-const UnstyledLink = (props: { children: ReactNode; to: string }) => {
-	return (
-		<Link to={props.to} style={{ all: "unset", cursor: "pointer" }}>
-			{props.children}
-		</Link>
-	);
-};
diff --git a/apps/frontend/app/routes/_dashboard.fitness.$action.tsx b/apps/frontend/app/routes/_dashboard.fitness.$action.tsx
index 4047f816c5..45a6b218e7 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.$action.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.$action.tsx
@@ -151,6 +151,8 @@ import {
 	useMeasurementsDrawerOpen,
 } from "~/lib/state/fitness";
 
+const DEFAULT_SET_TIMEOUT_DELAY = 800;
+
 export const loader = async ({ params }: LoaderFunctionArgs) => {
 	const { action } = zx.parseParams(params, {
 		action: z.nativeEnum(FitnessAction),
@@ -205,16 +207,31 @@ const getNextSetInWorkout = (
 			return { exerciseIdx, setIdx: setIdx, wasLastSet: areAllSetsConfirmed };
 		}
 	}
-	if (areAllSetsConfirmed)
-		return {
-			exerciseIdx: currentExerciseIdx + 1,
-			setIdx: 0,
-			wasLastSet: true,
-		};
+	if (areAllSetsConfirmed) {
+		for (
+			let i = currentExerciseIdx + 1;
+			i < currentWorkout.exercises.length;
+			i++
+		) {
+			const exerciseProgress =
+				getProgressOfExercise(currentWorkout, i) !== "complete";
+			if (exerciseProgress)
+				return {
+					setIdx: 0,
+					exerciseIdx: i,
+					wasLastSet: true,
+				};
+		}
+	}
+	const isLastSetOfLastExercise =
+		currentExerciseIdx === currentWorkout.exercises.length - 1 &&
+		currentSetIdx ===
+			currentWorkout.exercises[currentExerciseIdx].sets.length - 1;
+	if (isLastSetOfLastExercise) return { wasLastSet: true };
 	return {
-		exerciseIdx: currentExerciseIdx,
-		setIdx: currentSetIdx + 1,
 		wasLastSet: false,
+		setIdx: currentSetIdx + 1,
+		exerciseIdx: currentExerciseIdx,
 	};
 };
 
@@ -234,27 +251,33 @@ const usePerformTasksAfterSetConfirmed = () => {
 		const userExerciseDetails = await queryClient.ensureQueryData(
 			getUserExerciseDetailsQuery(exerciseId),
 		);
+		let exerciseIdxToFocusOn = undefined;
 		setCurrentWorkout((cw) =>
 			produce(cw, (draft) => {
 				if (!draft) return;
 				const currentExercise = draft.exercises[exerciseIdx];
 				const nextSet = getNextSetInWorkout(setIdx, exerciseIdx, draft);
-				focusOnExercise(nextSet.exerciseIdx);
+				exerciseIdxToFocusOn = nextSet.exerciseIdx;
 				if (nextSet.wasLastSet) {
 					currentExercise.isCollapsed = true;
 					currentExercise.isShowDetailsOpen = false;
-					const nextExercise = draft.exercises[nextSet.exerciseIdx];
-					const nextExerciseHasDetailsToShow =
-						nextExercise &&
-						exerciseHasDetailsToShow(exerciseDetails, userExerciseDetails);
-					if (nextExerciseHasDetailsToShow) {
-						nextExercise.isCollapsed = false;
-						if (userPreferences.fitness.logging.showDetailsWhileEditing)
-							nextExercise.isShowDetailsOpen = true;
+					if (isNumber(nextSet.exerciseIdx)) {
+						const nextExercise = draft.exercises[nextSet.exerciseIdx];
+						const nextExerciseHasDetailsToShow =
+							nextExercise &&
+							exerciseHasDetailsToShow(exerciseDetails, userExerciseDetails);
+						if (nextExerciseHasDetailsToShow) {
+							nextExercise.isCollapsed = false;
+							if (userPreferences.fitness.logging.showDetailsWhileEditing)
+								nextExercise.isShowDetailsOpen = true;
+						}
 					}
 				}
 			}),
 		);
+		if (isNumber(exerciseIdxToFocusOn)) {
+			focusOnExercise(exerciseIdxToFocusOn);
+		}
 	};
 
 	return performTask;
@@ -340,7 +363,7 @@ export default function Page() {
 				}
 				playCompleteTimerSound();
 				stopTimer();
-				setTimeout(() => closeTimerDrawer(), 500);
+				setTimeout(() => closeTimerDrawer(), DEFAULT_SET_TIMEOUT_DELAY);
 			}
 		}
 	}, 1000);
@@ -1186,7 +1209,7 @@ const focusOnExercise = (idx: number) => {
 	setTimeout(() => {
 		const exercise = document.getElementById(idx.toString());
 		exercise?.scrollIntoView({ behavior: "smooth" });
-	}, 800);
+	}, DEFAULT_SET_TIMEOUT_DELAY);
 };
 
 const exerciseHasDetailsToShow = (
diff --git a/apps/frontend/app/routes/_dashboard.fitness.$entity.list.tsx b/apps/frontend/app/routes/_dashboard.fitness.$entity.list.tsx
index 088cd5ddd4..b3e0c8d51b 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.$entity.list.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.$entity.list.tsx
@@ -228,11 +228,11 @@ const DisplayFitnessEntity = (props: { entityId: string; index: number }) => {
 							workoutTemplateId: props.entityId,
 						})
 						.then(({ userWorkoutTemplateDetails }) => ({
-							detail: "",
 							name: userWorkoutTemplateDetails.details.name,
 							summary: userWorkoutTemplateDetails.details.summary,
 							timestamp: userWorkoutTemplateDetails.details.createdOn,
 							information: userWorkoutTemplateDetails.details.information,
+							detail: `${userWorkoutTemplateDetails.details.information.exercises.length} exercises`,
 						})),
 				)
 				.exhaustive(),
@@ -277,12 +277,13 @@ const DisplayFitnessEntity = (props: { entityId: string; index: number }) => {
 							</Text>
 						</Group>
 						<Group mt="xs">
-							{entityInformation.detail ? (
-								<DisplayStat
-									icon={<IconClock size={16} />}
-									data={entityInformation.detail}
-								/>
-							) : null}
+							<DisplayStat
+								data={entityInformation.detail}
+								icon={match(loaderData.entity)
+									.with(FitnessEntity.Workouts, () => <IconClock size={16} />)
+									.with(FitnessEntity.Templates, () => <IconWeight size={16} />)
+									.exhaustive()}
+							/>
 							{entityInformation.summary.total ? (
 								<>
 									{personalBestsAchieved !== 0 ? (
diff --git a/apps/frontend/app/routes/_dashboard.fitness.exercises.list.tsx b/apps/frontend/app/routes/_dashboard.fitness.exercises.list.tsx
index 09e8411269..1202f29bc3 100644
--- a/apps/frontend/app/routes/_dashboard.fitness.exercises.list.tsx
+++ b/apps/frontend/app/routes/_dashboard.fitness.exercises.list.tsx
@@ -24,6 +24,7 @@ import {
 import {
 	type UseListStateHandlers,
 	useDisclosure,
+	useInViewport,
 	useListState,
 } from "@mantine/hooks";
 import type {
@@ -417,15 +418,18 @@ const ExerciseItemDisplay = (props: {
 	setMergingExercise: (value: string | null) => void;
 	setSelectedExercises: UseListStateHandlers<SelectExercise>;
 }) => {
-	const [currentWorkout, setCurrentWorkout] = useCurrentWorkout();
-	const navigate = useNavigate();
 	const submit = useSubmit();
-	const { data: exercise } = useQuery(
-		getExerciseDetailsQuery(props.exerciseId),
-	);
-	const { data: userExerciseDetails } = useQuery(
-		getUserExerciseDetailsQuery(props.exerciseId),
-	);
+	const navigate = useNavigate();
+	const [currentWorkout, setCurrentWorkout] = useCurrentWorkout();
+	const { ref, inViewport } = useInViewport();
+	const { data: exercise } = useQuery({
+		...getExerciseDetailsQuery(props.exerciseId),
+		enabled: inViewport,
+	});
+	const { data: userExerciseDetails } = useQuery({
+		...getUserExerciseDetailsQuery(props.exerciseId),
+		enabled: inViewport,
+	});
 
 	const firstMuscle = exercise?.muscles?.at(0);
 	const numTimesInteracted =
@@ -522,6 +526,6 @@ const ExerciseItemDisplay = (props: {
 			</Link>
 		</Flex>
 	) : (
-		<Skeleton height={56} />
+		<Skeleton height={56} ref={ref} />
 	);
 };
diff --git a/apps/frontend/app/routes/_dashboard.media.genre.list.tsx b/apps/frontend/app/routes/_dashboard.media.genre.list.tsx
index 8d2293e2b3..1751a52a17 100644
--- a/apps/frontend/app/routes/_dashboard.media.genre.list.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.genre.list.tsx
@@ -8,16 +8,17 @@ import {
 	Image,
 	Pagination,
 	Paper,
+	Skeleton,
 	Stack,
 	Text,
 	Title,
 } from "@mantine/core";
+import { useInViewport } from "@mantine/hooks";
 import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
 import { Link, useLoaderData } from "@remix-run/react";
 import {
 	GenreDetailsDocument,
 	GenresListDocument,
-	type GenresListQuery,
 } from "@ryot/generated/graphql/backend/graphql";
 import { getInitials, isString, truncate } from "@ryot/ts-utils";
 import { useQuery } from "@tanstack/react-query";
@@ -101,8 +102,8 @@ export default function Page() {
 							items found
 						</Box>
 						<ApplicationGrid>
-							{loaderData.genresList.items.map((genre) => (
-								<DisplayGenre key={genre.id} genre={genre} />
+							{loaderData.genresList.items.map((genreId) => (
+								<DisplayGenre key={genreId} genreId={genreId} />
 							))}
 						</ApplicationGrid>
 					</>
@@ -124,18 +125,16 @@ export default function Page() {
 	);
 }
 
-type Genre = GenresListQuery["genresList"]["items"][number];
-
-const DisplayGenre = (props: { genre: Genre }) => {
+const DisplayGenre = (props: { genreId: string }) => {
 	const coreDetails = useCoreDetails();
-	const color = useGetRandomMantineColor(props.genre.name);
-	const fallbackImageUrl = useFallbackImageUrl(getInitials(props.genre.name));
-	const { data: genreImages } = useQuery({
-		queryKey: queryFactory.media.genreImages(props.genre.id).queryKey,
+	const { ref, inViewport } = useInViewport();
+	const { data: genreData } = useQuery({
+		enabled: inViewport,
+		queryKey: queryFactory.media.genreImages(props.genreId).queryKey,
 		queryFn: async () => {
 			const { genreDetails } = await clientGqlService.request(
 				GenreDetailsDocument,
-				{ input: { genreId: props.genre.id } },
+				{ input: { genreId: props.genreId } },
 			);
 			let images = [];
 			for (const content of genreDetails.contents.items) {
@@ -146,27 +145,31 @@ const DisplayGenre = (props: { genre: Genre }) => {
 				if (isString(image)) images.push(image);
 			}
 			if (images.length < 4) images = images.splice(0, 1);
-			return images;
+			return { genreDetails, images };
 		},
 	});
 
-	return (
+	const genreName = genreData?.genreDetails.details.name || "";
+	const color = useGetRandomMantineColor(genreName);
+	const fallbackImageUrl = useFallbackImageUrl(getInitials(genreName));
+
+	return genreData ? (
 		<Anchor
 			component={Link}
-			to={$path("/media/genre/:id", { id: props.genre.id })}
+			to={$path("/media/genre/:id", { id: props.genreId })}
 		>
 			<Stack gap={4}>
 				<Box pos="relative">
 					{coreDetails.isServerKeyValidated ? (
 						<Paper radius="md" style={{ overflow: "hidden" }}>
 							<Flex h={260} w={168} wrap="wrap">
-								{genreImages?.map((image) => (
+								{genreData.images.map((image) => (
 									<Image
-										h={genreImages.length === 1 ? "auto" : 130}
-										w={genreImages.length === 1 ? "auto" : 84}
 										key={image}
 										src={image}
-										alt={props.genre.name}
+										alt={props.genreId}
+										h={genreData.images.length === 1 ? "auto" : 130}
+										w={genreData.images.length === 1 ? "auto" : 84}
 									/>
 								))}
 							</Flex>
@@ -174,9 +177,9 @@ const DisplayGenre = (props: { genre: Genre }) => {
 					) : (
 						<>
 							<Image
-								radius="md"
 								h={260}
-								alt={props.genre.name}
+								radius="md"
+								alt={genreName}
 								fallbackSrc={fallbackImageUrl}
 							/>
 							<Box pos="absolute" left={0} right={0} bottom={0}>
@@ -187,9 +190,11 @@ const DisplayGenre = (props: { genre: Genre }) => {
 				</Box>
 				<Group justify="center">
 					<Box h={11} w={11} bg={color} style={{ borderRadius: 2 }} />
-					<Text>{truncate(props.genre.name, { length: 13 })}</Text>
+					<Text>{truncate(genreName, { length: 13 })}</Text>
 				</Group>
 			</Stack>
 		</Anchor>
+	) : (
+		<Skeleton height={290} ref={ref} />
 	);
 };
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 0ab0deb05f..e690874fe9 100644
--- a/apps/frontend/app/routes/_dashboard.media.item.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.item.$id._index.tsx
@@ -83,6 +83,7 @@ import {
 } from "@tabler/icons-react";
 import type { HumanizeDurationOptions } from "humanize-duration-ts";
 import {
+	type FormEvent,
 	Fragment,
 	type ReactNode,
 	type RefObject,
@@ -286,7 +287,78 @@ export default function Page() {
 	const nextEntry = loaderData.userMetadataDetails.nextEntry;
 	const firstGroupAssociated = loaderData.metadataDetails.group.at(0);
 
-	const onSubmitProgressUpdate = (e: React.FormEvent<HTMLFormElement>) => {
+	const additionalMetadataDetails = [
+		userPreferences.featuresEnabled.media.groups && firstGroupAssociated && (
+			<Link
+				key="group-link"
+				style={{ color: "unset" }}
+				to={$path("/media/groups/item/:id", {
+					id: firstGroupAssociated.id,
+				})}
+			>
+				<Text c="dimmed" fs="italic" span>
+					{firstGroupAssociated.name} #{firstGroupAssociated.part}
+				</Text>
+			</Link>
+		),
+		loaderData.metadataDetails.publishDate
+			? dayjsLib(loaderData.metadataDetails.publishDate).format("LL")
+			: loaderData.metadataDetails.publishYear,
+		loaderData.metadataDetails.originalLanguage,
+		loaderData.metadataDetails.productionStatus,
+		loaderData.metadataDetails.bookSpecifics?.pages &&
+			`${loaderData.metadataDetails.bookSpecifics.pages} pages`,
+		loaderData.metadataDetails.podcastSpecifics?.totalEpisodes &&
+			`${loaderData.metadataDetails.podcastSpecifics.totalEpisodes} episodes`,
+		loaderData.metadataDetails.animeSpecifics?.episodes &&
+			`${loaderData.metadataDetails.animeSpecifics.episodes} episodes`,
+		loaderData.metadataDetails.mangaSpecifics?.chapters &&
+			`${loaderData.metadataDetails.mangaSpecifics.chapters} chapters`,
+		loaderData.metadataDetails.mangaSpecifics?.volumes &&
+			`${loaderData.metadataDetails.mangaSpecifics.volumes} volumes`,
+		loaderData.metadataDetails.movieSpecifics?.runtime &&
+			humanizeDuration(
+				dayjsLib
+					.duration(loaderData.metadataDetails.movieSpecifics.runtime, "minute")
+					.asMilliseconds(),
+			),
+		loaderData.metadataDetails.showSpecifics?.totalSeasons &&
+			`${loaderData.metadataDetails.showSpecifics.totalSeasons} seasons`,
+		loaderData.metadataDetails.showSpecifics?.totalEpisodes &&
+			`${loaderData.metadataDetails.showSpecifics.totalEpisodes} episodes`,
+		loaderData.metadataDetails.showSpecifics?.runtime &&
+			humanizeDuration(
+				dayjsLib
+					.duration(loaderData.metadataDetails.showSpecifics.runtime, "minute")
+					.asMilliseconds(),
+			),
+		loaderData.metadataDetails.audioBookSpecifics?.runtime &&
+			humanizeDuration(
+				dayjsLib
+					.duration(
+						loaderData.metadataDetails.audioBookSpecifics.runtime,
+						"minute",
+					)
+					.asMilliseconds(),
+			),
+		loaderData.metadataDetails.musicSpecifics?.duration &&
+			humanizeDuration(
+				dayjsLib
+					.duration(
+						loaderData.metadataDetails.musicSpecifics.duration,
+						"second",
+					)
+					.asMilliseconds(),
+			),
+		loaderData.metadataDetails.musicSpecifics?.viewCount &&
+			formatQuantityWithCompactNotation(
+				loaderData.metadataDetails.musicSpecifics.viewCount,
+			),
+		loaderData.metadataDetails.musicSpecifics?.byVariousArtists &&
+			"Various Artists",
+	].filter(Boolean);
+
+	const onSubmitProgressUpdate = (e: FormEvent<HTMLFormElement>) => {
 		submit(e);
 		events.updateProgress(loaderData.metadataDetails.title);
 		refreshUserMetadataDetails(loaderData.metadataId);
@@ -372,90 +444,13 @@ export default function Page() {
 							))}
 						</Group>
 					) : null}
-					<Box>
-						{userPreferences.featuresEnabled.media.groups &&
-						firstGroupAssociated ? (
-							<Link
-								style={{ color: "unset" }}
-								to={$path("/media/groups/item/:id", {
-									id: firstGroupAssociated.id,
-								})}
-							>
-								<Text c="dimmed" fs="italic">
-									{firstGroupAssociated.name} #{firstGroupAssociated.part}
-								</Text>
-							</Link>
-						) : null}
-						<Text c="dimmed" fz={{ base: "sm", lg: "md" }}>
-							{[
-								loaderData.metadataDetails.publishDate
-									? dayjsLib(loaderData.metadataDetails.publishDate).format(
-											"LL",
-										)
-									: loaderData.metadataDetails.publishYear,
-								loaderData.metadataDetails.originalLanguage,
-								loaderData.metadataDetails.productionStatus,
-								loaderData.metadataDetails.bookSpecifics?.pages &&
-									`${loaderData.metadataDetails.bookSpecifics.pages} pages`,
-								loaderData.metadataDetails.podcastSpecifics?.totalEpisodes &&
-									`${loaderData.metadataDetails.podcastSpecifics.totalEpisodes} episodes`,
-								loaderData.metadataDetails.animeSpecifics?.episodes &&
-									`${loaderData.metadataDetails.animeSpecifics.episodes} episodes`,
-								loaderData.metadataDetails.mangaSpecifics?.chapters &&
-									`${loaderData.metadataDetails.mangaSpecifics.chapters} chapters`,
-								loaderData.metadataDetails.mangaSpecifics?.volumes &&
-									`${loaderData.metadataDetails.mangaSpecifics.volumes} volumes`,
-								loaderData.metadataDetails.movieSpecifics?.runtime &&
-									humanizeDuration(
-										dayjsLib
-											.duration(
-												loaderData.metadataDetails.movieSpecifics.runtime,
-												"minute",
-											)
-											.asMilliseconds(),
-									),
-								loaderData.metadataDetails.showSpecifics?.totalSeasons &&
-									`${loaderData.metadataDetails.showSpecifics.totalSeasons} seasons`,
-								loaderData.metadataDetails.showSpecifics?.totalEpisodes &&
-									`${loaderData.metadataDetails.showSpecifics.totalEpisodes} episodes`,
-								loaderData.metadataDetails.showSpecifics?.runtime &&
-									humanizeDuration(
-										dayjsLib
-											.duration(
-												loaderData.metadataDetails.showSpecifics.runtime,
-												"minute",
-											)
-											.asMilliseconds(),
-									),
-								loaderData.metadataDetails.audioBookSpecifics?.runtime &&
-									humanizeDuration(
-										dayjsLib
-											.duration(
-												loaderData.metadataDetails.audioBookSpecifics.runtime,
-												"minute",
-											)
-											.asMilliseconds(),
-									),
-								loaderData.metadataDetails.musicSpecifics?.duration &&
-									humanizeDuration(
-										dayjsLib
-											.duration(
-												loaderData.metadataDetails.musicSpecifics.duration,
-												"second",
-											)
-											.asMilliseconds(),
-									),
-								loaderData.metadataDetails.musicSpecifics?.viewCount &&
-									formatQuantityWithCompactNotation(
-										loaderData.metadataDetails.musicSpecifics.viewCount,
-									),
-								loaderData.metadataDetails.musicSpecifics?.byVariousArtists &&
-									"Various Artists",
-							]
-								.filter(Boolean)
-								.join(" • ")}
-						</Text>
-					</Box>
+					<Text c="dimmed" fz={{ base: "sm", lg: "md" }}>
+						{additionalMetadataDetails.length > 0
+							? additionalMetadataDetails
+									.map<ReactNode>((s) => s)
+									.reduce((prev, curr) => [prev, " • ", curr])
+							: null}
+					</Text>
 					{loaderData.metadataDetails.providerRating ||
 					loaderData.userMetadataDetails.averageRating ? (
 						<Group>
@@ -1078,16 +1073,14 @@ export default function Page() {
 							loaderData.userMetadataDetails.showProgress ? (
 								<Virtuoso
 									data={loaderData.metadataDetails.showSpecifics.seasons}
-									itemContent={(seasonIdx, season) => {
-										return (
-											<DisplayShowSeason
-												season={season}
-												seasonIdx={seasonIdx}
-												key={season.seasonNumber}
-												openSeasonModal={() => setOpenedShowSeason(seasonIdx)}
-											/>
-										);
-									}}
+									itemContent={(seasonIdx, season) => (
+										<DisplayShowSeason
+											season={season}
+											seasonIdx={seasonIdx}
+											key={season.seasonNumber}
+											openSeasonModal={() => setOpenedShowSeason(seasonIdx)}
+										/>
+									)}
 								/>
 							) : null}
 						</Tabs.Panel>
@@ -1872,12 +1865,13 @@ const DisplayShowSeason = (props: {
 	season: Season;
 	seasonIdx: number;
 	openSeasonModal: () => void;
-	seasonProgress?: SeasonProgress;
 }) => {
 	const loaderData = useLoaderData<typeof loader>();
 	const [_, setMetadataToUpdate] = useMetadataProgressUpdate();
 
-	const numTimesSeen = props.seasonProgress?.timesSeen || 0;
+	const seasonProgress =
+		loaderData.userMetadataDetails.showProgress?.[props.seasonIdx];
+	const numTimesSeen = seasonProgress?.timesSeen || 0;
 	const isSeen = numTimesSeen > 0;
 
 	return (
diff --git a/apps/frontend/app/routes/_dashboard.settings.imports-and-exports._index.tsx b/apps/frontend/app/routes/_dashboard.settings.imports-and-exports._index.tsx
index 652cfcde65..6b50b04714 100644
--- a/apps/frontend/app/routes/_dashboard.settings.imports-and-exports._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.settings.imports-and-exports._index.tsx
@@ -1,6 +1,7 @@
 import { CodeHighlight } from "@mantine/code-highlight";
 import {
 	Accordion,
+	ActionIcon,
 	Anchor,
 	Box,
 	Button,
@@ -40,9 +41,10 @@ import {
 	kebabCase,
 	processSubmission,
 } from "@ryot/ts-utils";
-import { IconDownload } from "@tabler/icons-react";
+import { IconDownload, IconTrash } from "@tabler/icons-react";
 import { filesize } from "filesize";
 import { useState } from "react";
+import { $path } from "remix-routes";
 import { match } from "ts-pattern";
 import { withFragment, withQuery } from "ufo";
 import { z } from "zod";
@@ -124,15 +126,12 @@ export const action = async ({ request }: ActionFunctionArgs) => {
 				DeployImportJobDocument,
 				{ input: { source, ...values } },
 			);
-			return Response.json(
-				{ status: "success", generateAuthToken: false } as const,
-				{
-					headers: await createToastHeaders({
-						type: "success",
-						message: "Import job started in the background",
-					}),
-				},
-			);
+			return Response.json({ status: "success" } as const, {
+				headers: await createToastHeaders({
+					type: "success",
+					message: "Import job started in the background",
+				}),
+			});
 		})
 		.with("deployExport", async () => {
 			await serverGqlService.authenticatedRequest(
@@ -140,15 +139,12 @@ export const action = async ({ request }: ActionFunctionArgs) => {
 				DeployExportJobDocument,
 				{},
 			);
-			return Response.json(
-				{ status: "success", generateAuthToken: false } as const,
-				{
-					headers: await createToastHeaders({
-						type: "success",
-						message: "Export job started in the background",
-					}),
-				},
-			);
+			return Response.json({ status: "success" } as const, {
+				headers: await createToastHeaders({
+					type: "success",
+					message: "Export job started in the background",
+				}),
+			});
 		})
 		.run();
 };
@@ -585,11 +581,40 @@ export default function Page() {
 														({filesize(exp.size)})
 													</Text>
 												</Group>
-												<Anchor href={exp.url} target="_blank" rel="noreferrer">
-													<ThemeIcon color="blue" variant="transparent">
-														<IconDownload />
-													</ThemeIcon>
-												</Anchor>
+												<Group>
+													<Anchor
+														href={exp.url}
+														target="_blank"
+														rel="noreferrer"
+													>
+														<ThemeIcon color="blue" variant="transparent">
+															<IconDownload />
+														</ThemeIcon>
+													</Anchor>
+													<Form
+														method="POST"
+														action={withQuery($path("/actions"), {
+															intent: "deleteS3Asset",
+														})}
+													>
+														<input hidden name="key" defaultValue={exp.key} />
+														<ActionIcon
+															color="red"
+															type="submit"
+															variant="transparent"
+															onClick={(e) => {
+																const form = e.currentTarget.form;
+																e.preventDefault();
+																openConfirmationModal(
+																	"Are you sure you want to delete this export? This action is irreversible.",
+																	() => submit(form),
+																);
+															}}
+														>
+															<IconTrash />
+														</ActionIcon>
+													</Form>
+												</Group>
 											</Group>
 										</Box>
 									))}
diff --git a/apps/frontend/app/routes/_dashboard.settings.integrations.tsx b/apps/frontend/app/routes/_dashboard.settings.integrations.tsx
index bb504ec7c8..1aba836752 100644
--- a/apps/frontend/app/routes/_dashboard.settings.integrations.tsx
+++ b/apps/frontend/app/routes/_dashboard.settings.integrations.tsx
@@ -2,6 +2,7 @@ import { useAutoAnimate } from "@formkit/auto-animate/react";
 import {
 	ActionIcon,
 	Alert,
+	Anchor,
 	Box,
 	Button,
 	Checkbox,
@@ -65,18 +66,28 @@ import {
 } from "~/lib/hooks";
 import { createToastHeaders, serverGqlService } from "~/lib/utilities.server";
 
-const PRO_INTEGRATIONS = [IntegrationProvider.JellyfinPush];
+const PRO_INTEGRATIONS = [
+	IntegrationProvider.JellyfinPush,
+	IntegrationProvider.YoutubeMusic,
+];
 const YANK_INTEGRATIONS = [
-	IntegrationProvider.Audiobookshelf,
 	IntegrationProvider.Komga,
 	IntegrationProvider.PlexYank,
+	IntegrationProvider.YoutubeMusic,
+	IntegrationProvider.Audiobookshelf,
 ];
 const PUSH_INTEGRATIONS = [
 	IntegrationProvider.Radarr,
 	IntegrationProvider.Sonarr,
 	IntegrationProvider.JellyfinPush,
 ];
+const SYNC_TO_OWNED_COLLECTION_INTEGRATIONS = [
+	IntegrationProvider.Komga,
+	IntegrationProvider.PlexYank,
+	IntegrationProvider.Audiobookshelf,
+];
 const NO_SHOW_URL = [...YANK_INTEGRATIONS, ...PUSH_INTEGRATIONS];
+const NO_PROGRESS_ADJUSTMENT = [...PUSH_INTEGRATIONS];
 
 export const loader = async ({ request }: LoaderFunctionArgs) => {
 	const [{ userIntegrations }] = await Promise.all([
@@ -193,6 +204,7 @@ const createSchema = z.object({
 			jellyfinPushBaseUrl: z.string().optional(),
 			jellyfinPushUsername: z.string().optional(),
 			jellyfinPushPassword: z.string().optional(),
+			youtubeMusicAuthCookie: z.string().optional(),
 		})
 		.optional(),
 });
@@ -404,17 +416,18 @@ const CreateIntegrationModal = (props: {
 }) => {
 	const coreDetails = useCoreDetails();
 	const [provider, setProvider] = useState<IntegrationProvider>();
-	const disableCreationButton =
+
+	const disableCreationButtonBecauseProRequired =
 		!coreDetails.isServerKeyValidated &&
 		provider &&
 		PRO_INTEGRATIONS.includes(provider);
 
 	return (
 		<Modal
-			opened={props.createModalOpened}
-			onClose={props.closeIntegrationModal}
 			centered
 			withCloseButton={false}
+			opened={props.createModalOpened}
+			onClose={props.closeIntegrationModal}
 		>
 			<Form
 				replace
@@ -424,36 +437,37 @@ const CreateIntegrationModal = (props: {
 			>
 				<Stack>
 					<Select
-						label="Select a provider"
-						name="provider"
 						required
+						searchable
+						name="provider"
+						label="Select a provider"
+						onChange={(e) => setProvider(e as IntegrationProvider)}
 						data={Object.values(IntegrationProvider).map((is) => ({
 							label: changeCase(is),
 							value: is,
 						}))}
-						onChange={(e) => setProvider(e as IntegrationProvider)}
 					/>
-					{provider && !PUSH_INTEGRATIONS.includes(provider) ? (
+					{provider && !NO_PROGRESS_ADJUSTMENT.includes(provider) ? (
 						<Group wrap="nowrap">
 							<NumberInput
-								size="xs"
-								label="Minimum progress"
-								description="Progress will not be synced below this value"
+								min={0}
 								required
+								max={100}
+								size="xs"
 								name="minimumProgress"
+								label="Minimum progress"
 								defaultValue={MINIMUM_PROGRESS}
-								min={0}
-								max={100}
+								description="Progress will not be synced below this value"
 							/>
 							<NumberInput
-								size="xs"
-								label="Maximum progress"
-								description="After this value, progress will be marked as completed"
+								min={0}
 								required
+								max={100}
+								size="xs"
 								name="maximumProgress"
+								label="Maximum progress"
 								defaultValue={MAXIMUM_PROGRESS}
-								min={0}
-								max={100}
+								description="After this value, progress will be marked as completed"
 							/>
 						</Group>
 					) : null}
@@ -514,6 +528,28 @@ const CreateIntegrationModal = (props: {
 								/>
 							</>
 						))
+						.with(IntegrationProvider.YoutubeMusic, () => (
+							<>
+								<TextInput
+									required
+									label="Auth Cookie"
+									name="providerSpecifics.youtubeMusicAuthCookie"
+									description={
+										<Text size="xs" c="dimmed">
+											Please follow the{" "}
+											<Anchor
+												target="_blank"
+												rel="noreferrer noopener"
+												href="https://docs.ryot.io/integrations#youtube-music"
+											>
+												docs
+											</Anchor>{" "}
+											to get the correct cookie
+										</Text>
+									}
+								/>
+							</>
+						))
 						.with(IntegrationProvider.PlexSink, () => (
 							<>
 								<TextInput
@@ -544,7 +580,8 @@ const CreateIntegrationModal = (props: {
 						.with(IntegrationProvider.Radarr, () => <ArrInputs name="radarr" />)
 						.with(IntegrationProvider.Sonarr, () => <ArrInputs name="sonarr" />)
 						.otherwise(() => undefined)}
-					{provider && YANK_INTEGRATIONS.includes(provider) ? (
+					{provider &&
+					SYNC_TO_OWNED_COLLECTION_INTEGRATIONS.includes(provider) ? (
 						<Tooltip
 							label="Only available for Pro users"
 							disabled={coreDetails.isServerKeyValidated}
@@ -560,9 +597,12 @@ const CreateIntegrationModal = (props: {
 					) : undefined}
 					<Tooltip
 						label={PRO_REQUIRED_MESSAGE}
-						disabled={!disableCreationButton}
+						disabled={!disableCreationButtonBecauseProRequired}
 					>
-						<Button type="submit" disabled={disableCreationButton}>
+						<Button
+							type="submit"
+							disabled={disableCreationButtonBecauseProRequired}
+						>
 							Submit
 						</Button>
 					</Tooltip>
@@ -637,7 +677,7 @@ const UpdateIntegrationModal = (props: {
 						defaultValue={props.updateIntegrationData.id}
 					/>
 					<Stack>
-						{!PUSH_INTEGRATIONS.includes(
+						{!NO_PROGRESS_ADJUSTMENT.includes(
 							props.updateIntegrationData.provider,
 						) ? (
 							<Group wrap="nowrap">
@@ -668,7 +708,7 @@ const UpdateIntegrationModal = (props: {
 								props.updateIntegrationData.isDisabled || undefined
 							}
 						/>
-						{YANK_INTEGRATIONS.includes(
+						{SYNC_TO_OWNED_COLLECTION_INTEGRATIONS.includes(
 							props.updateIntegrationData.provider,
 						) ? (
 							<Checkbox
diff --git a/apps/frontend/app/routes/_dashboard.settings.preferences.tsx b/apps/frontend/app/routes/_dashboard.settings.preferences.tsx
index 57b80ce477..841081eb51 100644
--- a/apps/frontend/app/routes/_dashboard.settings.preferences.tsx
+++ b/apps/frontend/app/routes/_dashboard.settings.preferences.tsx
@@ -9,6 +9,7 @@ import {
 	Group,
 	Input,
 	JsonInput,
+	MultiSelect,
 	NumberInput,
 	Paper,
 	SegmentedControl,
@@ -35,7 +36,14 @@ import {
 	UserReviewScale,
 	UserUnitSystem,
 } from "@ryot/generated/graphql/backend/graphql";
-import { changeCase, cn, isNumber, snakeCase, startCase } from "@ryot/ts-utils";
+import {
+	changeCase,
+	cn,
+	isBoolean,
+	isNumber,
+	snakeCase,
+	startCase,
+} from "@ryot/ts-utils";
 import { IconCheckbox } from "@tabler/icons-react";
 import {
 	IconAlertCircle,
@@ -209,31 +217,56 @@ export default function Page() {
 						<Stack>
 							<Text>Features that you want to use.</Text>
 							{(["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"
-													label={changeCase(snakeCase(name))}
-													defaultChecked={isEnabled}
-													disabled={!!isEditDisabled}
-													onChange={(ev) => {
-														updatePreference((draft) => {
-															// biome-ignore lint/suspicious/noExplicitAny: too much work to use correct types
-															(draft as any).featuresEnabled[facet][name] =
-																ev.currentTarget.checked;
-														});
+								(facet) => {
+									const entries = Object.entries(
+										userPreferences.featuresEnabled[facet],
+									);
+
+									return (
+										<Fragment key={facet}>
+											<Title order={4}>{startCase(facet)}</Title>
+											<SimpleGrid cols={2}>
+												{entries.map(([name, isEnabled]) =>
+													isBoolean(isEnabled) ? (
+														<Switch
+															key={name}
+															size="xs"
+															defaultChecked={isEnabled}
+															disabled={!!isEditDisabled}
+															label={changeCase(snakeCase(name))}
+															onChange={(ev) => {
+																updatePreference((draft) => {
+																	// biome-ignore lint/suspicious/noExplicitAny: too much work to use correct types
+																	(draft as any).featuresEnabled[facet][name] =
+																		ev.currentTarget.checked;
+																});
+															}}
+														/>
+													) : null,
+												)}
+											</SimpleGrid>
+											{facet === "media" ? (
+												<MultiSelect
+													defaultValue={
+														userPreferences.featuresEnabled[facet].specific
+													}
+													data={Object.values(MediaLot).map((lot) => ({
+														value: lot,
+														label: changeCase(lot),
+													}))}
+													onChange={(val) => {
+														if (val) {
+															updatePreference((draft) => {
+																draft.featuresEnabled[facet].specific =
+																	val as MediaLot[];
+															});
+														}
 													}}
 												/>
-											))}
-										</SimpleGrid>
-									</Fragment>
-								),
+											) : null}
+										</Fragment>
+									);
+								},
 							)}
 						</Stack>
 					</Tabs.Panel>
@@ -544,7 +577,7 @@ export default function Page() {
 								</SimpleGrid>
 							</Input.Wrapper>
 							<Divider />
-							<Title order={4}>Current workout</Title>
+							<Title order={4}>Workout logging</Title>
 							{(
 								[
 									"muteSounds",
@@ -555,7 +588,7 @@ export default function Page() {
 								const [label, isGatedBehindServerKeyValidation] = match(option)
 									.with(
 										"muteSounds",
-										() => ["Mute sounds while logging workouts"] as const,
+										() => ["Mute all sounds for actions"] as const,
 									)
 									.with(
 										"promptForRestTimer",
diff --git a/apps/frontend/app/routes/_dashboard.tsx b/apps/frontend/app/routes/_dashboard.tsx
index 36642be3ec..c750bdf5b8 100644
--- a/apps/frontend/app/routes/_dashboard.tsx
+++ b/apps/frontend/app/routes/_dashboard.tsx
@@ -107,7 +107,7 @@ import {
 import { useQuery } from "@tanstack/react-query";
 import { produce } from "immer";
 import Cookies from "js-cookie";
-import { type FormEvent, useState } from "react";
+import { type FC, type FormEvent, type ReactNode, useState } from "react";
 import { Fragment } from "react/jsx-runtime";
 import { $path } from "remix-routes";
 import { ClientOnly } from "remix-utils/client-only";
@@ -119,7 +119,6 @@ import {
 	ThreePointSmileyRating,
 	Verb,
 	convertDecimalToThreePointSmiley,
-	getLot,
 	getMetadataDetailsQuery,
 	getVerb,
 	refreshUserMetadataDetails,
@@ -171,17 +170,9 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
 	);
 
 	const mediaLinks = [
-		...(Object.entries(userPreferences.featuresEnabled.media || {})
-			.filter(([v, _]) => v !== "enabled")
-			.filter(([name, _]) => getLot(name) !== undefined)
-			.map(([name, enabled]) => {
-				// biome-ignore lint/style/noNonNullAssertion: required here
-				return { name: getLot(name)!, enabled };
-			})
-			?.filter((f) => f.enabled)
-			.map((f) => {
-				return { label: f.name, href: undefined };
-			}) || []),
+		...userPreferences.featuresEnabled.media.specific.map((f) => {
+			return { label: f, href: undefined };
+		}),
 		userPreferences.featuresEnabled.media.groups
 			? {
 					label: "Groups",
@@ -822,7 +813,7 @@ export default function Layout() {
 
 interface LinksGroupProps {
 	// biome-ignore lint/suspicious/noExplicitAny: required here
-	icon: React.FC<any>;
+	icon: FC<any>;
 	label: string;
 	href?: string;
 	opened: boolean;
@@ -1405,7 +1396,7 @@ const ReviewEntityForm = ({
 	});
 
 	const SmileySurround = (props: {
-		children: React.ReactNode;
+		children: ReactNode;
 		smileyRating: ThreePointSmileyRating;
 	}) => (
 		<ThemeIcon
diff --git a/apps/website/app/routes/features.tsx b/apps/website/app/routes/features.tsx
index 89fe233054..90deb52b86 100644
--- a/apps/website/app/routes/features.tsx
+++ b/apps/website/app/routes/features.tsx
@@ -24,6 +24,7 @@ import {
 	LucidePackageOpen,
 	LucideProjector,
 	LucideRefreshCcwDot,
+	LucideRouter,
 	LucideRuler,
 	LucideScale3D,
 	LucideShare,
@@ -198,6 +199,11 @@ const dataToDisplay = [
 				icon: LucideCandy,
 				text: "Suggestions that cater to your tastes based on your watch history",
 			},
+			{
+				icon: LucideRouter,
+				text: "Integrations with Youtube Music and Jellyfin for your music collection",
+				isPro: true,
+			},
 		],
 	},
 	{
diff --git a/crates/migrations/src/lib.rs b/crates/migrations/src/lib.rs
index 6a7f861484..70459f1218 100644
--- a/crates/migrations/src/lib.rs
+++ b/crates/migrations/src/lib.rs
@@ -61,6 +61,7 @@ mod m20241227_changes_for_issue_1157;
 mod m20241228_changes_for_issue_1130;
 mod m20241229_changes_for_issue_1155;
 mod m20250112_changes_for_issue_1174;
+mod m20250113_changes_for_issue_1169;
 
 pub use m20230410_create_metadata::Metadata as AliasedMetadata;
 pub use m20230413_create_person::Person as AliasedPerson;
@@ -143,6 +144,7 @@ impl MigratorTrait for Migrator {
             Box::new(m20241228_changes_for_issue_1130::Migration),
             Box::new(m20241229_changes_for_issue_1155::Migration),
             Box::new(m20250112_changes_for_issue_1174::Migration),
+            Box::new(m20250113_changes_for_issue_1169::Migration),
         ]
     }
 }
diff --git a/crates/migrations/src/m20250113_changes_for_issue_1169.rs b/crates/migrations/src/m20250113_changes_for_issue_1169.rs
new file mode 100644
index 0000000000..2daa30e3e9
--- /dev/null
+++ b/crates/migrations/src/m20250113_changes_for_issue_1169.rs
@@ -0,0 +1,38 @@
+use sea_orm_migration::prelude::*;
+
+#[derive(DeriveMigrationName)]
+pub struct Migration;
+
+#[async_trait::async_trait]
+impl MigrationTrait for Migration {
+    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
+        let db = manager.get_connection();
+        db.execute_unprepared(
+            r#"
+UPDATE "user"
+SET "preferences" = jsonb_set(
+    "preferences",
+    '{features_enabled,media,specific}',
+    '[
+        "book",
+        "show",
+        "movie",
+        "anime",
+        "manga",
+        "music",
+        "podcast",
+        "audio_book",
+        "video_game",
+        "visual_novel"
+    ]'::jsonb
+);
+            "#,
+        )
+        .await?;
+        Ok(())
+    }
+
+    async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
+        Ok(())
+    }
+}
diff --git a/crates/models/common/src/lib.rs b/crates/models/common/src/lib.rs
index 30edaf773f..a060553534 100644
--- a/crates/models/common/src/lib.rs
+++ b/crates/models/common/src/lib.rs
@@ -154,7 +154,16 @@ pub struct NamedObject {
 }
 
 #[derive(
-    Clone, Debug, Default, PartialEq, InputObject, FromJsonQueryResult, Eq, Serialize, Deserialize,
+    Eq,
+    Clone,
+    Hash,
+    Debug,
+    Default,
+    PartialEq,
+    Serialize,
+    Deserialize,
+    InputObject,
+    FromJsonQueryResult,
 )]
 pub struct SearchInput {
     pub take: Option<u64>,
@@ -197,6 +206,7 @@ pub enum UserNotificationContent {
 pub struct ExportJob {
     pub size: i64,
     pub url: String,
+    pub key: String,
     pub ended_at: DateTimeUtc,
     pub started_at: DateTimeUtc,
 }
@@ -224,7 +234,7 @@ pub struct DailyUserActivityHourRecord {
 /// The start date must be before the end date.
 #[skip_serializing_none]
 #[derive(
-    Debug, Default, Serialize, Deserialize, SimpleObject, InputObject, Clone, Eq, PartialEq,
+    Debug, Hash, Default, Serialize, Deserialize, SimpleObject, InputObject, Clone, Eq, PartialEq,
 )]
 #[graphql(input_name = "ApplicationDateRangeInput")]
 pub struct ApplicationDateRange {
@@ -232,7 +242,7 @@ pub struct ApplicationDateRange {
     pub start_date: Option<NaiveDate>,
 }
 
-#[derive(Debug, Serialize, Deserialize, Enum, Clone, Copy, Eq, PartialEq, Display)]
+#[derive(Debug, Hash, Serialize, Deserialize, Enum, Clone, Copy, Eq, PartialEq, Display)]
 #[strum(serialize_all = "snake_case")]
 pub enum DailyUserActivitiesResponseGroupedBy {
     Day,
@@ -242,14 +252,16 @@ pub enum DailyUserActivitiesResponseGroupedBy {
 }
 
 #[skip_serializing_none]
-#[derive(Debug, Default, Serialize, Deserialize, InputObject, Clone, PartialEq, Eq)]
+#[derive(Debug, Hash, Default, Serialize, Deserialize, InputObject, Clone, PartialEq, Eq)]
 pub struct UserAnalyticsInput {
     pub date_range: ApplicationDateRange,
     pub group_by: Option<DailyUserActivitiesResponseGroupedBy>,
 }
 
 #[skip_serializing_none]
-#[derive(Clone, Debug, PartialEq, InputObject, FromJsonQueryResult, Eq, Serialize, Deserialize)]
+#[derive(
+    Clone, Hash, Debug, PartialEq, InputObject, FromJsonQueryResult, Eq, Serialize, Deserialize,
+)]
 pub struct MetadataGroupSearchInput {
     pub lot: MediaLot,
     pub source: MediaSource,
@@ -279,7 +291,9 @@ pub struct PersonSourceSpecifics {
 }
 
 #[skip_serializing_none]
-#[derive(Clone, Debug, PartialEq, InputObject, FromJsonQueryResult, Eq, Serialize, Deserialize)]
+#[derive(
+    Clone, Hash, Debug, PartialEq, InputObject, FromJsonQueryResult, Eq, Serialize, Deserialize,
+)]
 pub struct PeopleSearchInput {
     pub search: SearchInput,
     pub source: MediaSource,
@@ -287,7 +301,9 @@ pub struct PeopleSearchInput {
 }
 
 #[skip_serializing_none]
-#[derive(Clone, Debug, PartialEq, InputObject, FromJsonQueryResult, Eq, Serialize, Deserialize)]
+#[derive(
+    Clone, Hash, Debug, PartialEq, InputObject, FromJsonQueryResult, Eq, Serialize, Deserialize,
+)]
 pub struct MetadataSearchInput {
     pub lot: MediaLot,
     pub search: SearchInput,
@@ -295,21 +311,21 @@ pub struct MetadataSearchInput {
 }
 
 #[skip_serializing_none]
-#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+#[derive(Clone, Hash, Debug, PartialEq, Eq, Serialize, Deserialize)]
 pub struct UserLevelCacheKey<T> {
     pub input: T,
     pub user_id: String,
 }
 
 #[skip_serializing_none]
-#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+#[derive(Clone, Hash, Debug, PartialEq, Eq, Serialize, Deserialize)]
 pub struct MetadataRecentlyConsumedCacheInput {
     pub entity_id: String,
     pub entity_lot: EntityLot,
 }
 
 #[skip_serializing_none]
-#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+#[derive(Clone, Hash, Debug, PartialEq, Eq, Serialize, Deserialize)]
 pub struct ProgressUpdateCacheInput {
     pub metadata_id: String,
     pub show_season_number: Option<i32>,
@@ -322,19 +338,29 @@ pub struct ProgressUpdateCacheInput {
 }
 
 #[skip_serializing_none]
-#[derive(Clone, Debug, PartialEq, FromJsonQueryResult, Eq, Serialize, Deserialize)]
+#[derive(Clone, Hash, Debug, PartialEq, Eq, Serialize, Deserialize)]
+pub struct YoutubeMusicSongListened {
+    pub id: String,
+    pub listened_on: NaiveDate,
+}
+
+#[skip_serializing_none]
+#[derive(
+    Clone, Hash, Debug, PartialEq, FromJsonQueryResult, Eq, Serialize, Deserialize, Display,
+)]
 pub enum ApplicationCacheKey {
     CoreDetails,
     IgdbSettings,
     TmdbSettings,
     ListennotesSettings,
     UserCollectionsList(UserLevelCacheKey<()>),
-    UserRecommendationsKey(UserLevelCacheKey<()>),
     UserAnalyticsParameters(UserLevelCacheKey<()>),
+    UserMetadataRecommendations(UserLevelCacheKey<()>),
     PeopleSearch(UserLevelCacheKey<PeopleSearchInput>),
     UserAnalytics(UserLevelCacheKey<UserAnalyticsInput>),
     MetadataSearch(UserLevelCacheKey<MetadataSearchInput>),
     MetadataGroupSearch(UserLevelCacheKey<MetadataGroupSearchInput>),
     ProgressUpdateCache(UserLevelCacheKey<ProgressUpdateCacheInput>),
+    YoutubeMusicSongListened(UserLevelCacheKey<YoutubeMusicSongListened>),
     MetadataRecentlyConsumed(UserLevelCacheKey<MetadataRecentlyConsumedCacheInput>),
 }
diff --git a/crates/models/dependent/src/lib.rs b/crates/models/dependent/src/lib.rs
index d95eee2910..813b9598e7 100644
--- a/crates/models/dependent/src/lib.rs
+++ b/crates/models/dependent/src/lib.rs
@@ -47,9 +47,6 @@ use strum::Display;
     name = "MetadataGroupSearchResults",
     params(media_models::MetadataGroupSearchItem)
 ))]
-#[graphql(concrete(name = "GenreListResults", params(media_models::GenreListItem)))]
-#[graphql(concrete(name = "WorkoutListResults", params(workout::Model)))]
-#[graphql(concrete(name = "WorkoutTemplateListResults", params(workout_template::Model)))]
 #[graphql(concrete(name = "IdResults", params(String)))]
 pub struct SearchResults<T: OutputType> {
     pub details: SearchDetails,
@@ -342,6 +339,8 @@ pub struct DailyUserActivityItem {
     pub manga_count: i64,
     pub movie_count: i64,
     pub total_movie_duration: i64,
+    pub music_count: i64,
+    pub total_music_duration: i64,
     pub show_count: i64,
     pub total_show_duration: i64,
     pub video_game_count: i64,
@@ -430,46 +429,35 @@ pub struct TmdbSettings {
     pub languages: Vec<TmdbLanguage>,
 }
 
-#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
-pub struct ListennotesSettings {
-    pub genres: HashMap<i32, String>,
-}
-
-#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
-pub struct IgdbSettings {
-    pub access_token: String,
-}
-
-#[skip_serializing_none]
-#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize, Clone)]
-pub struct UserRecommendationsKey {
-    pub recommendations_key: String,
-}
-
 #[skip_serializing_none]
 #[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize, Clone)]
 pub struct EmptyCacheValue {
     pub _empty: (),
 }
 
+pub type IgdbSettings = String;
+pub type YoutubeMusicSongListenedResponse = bool;
+pub type ListennotesSettings = HashMap<i32, String>;
 pub type UserCollectionsListResponse = Vec<CollectionItem>;
+pub type UserMetadataRecommendationsResponse = Vec<String>;
 pub type PeopleSearchResponse = SearchResults<PeopleSearchItem>;
 pub type MetadataSearchResponse = SearchResults<MetadataSearchItem>;
 pub type MetadataGroupSearchResponse = SearchResults<MetadataGroupSearchItem>;
 
 #[derive(Clone, Debug, PartialEq, FromJsonQueryResult, Serialize, Deserialize, Eq)]
-#[serde(untagged)]
 pub enum ApplicationCacheValue {
-    Empty(EmptyCacheValue),
     TmdbSettings(TmdbSettings),
     IgdbSettings(IgdbSettings),
     UserAnalytics(UserAnalytics),
     CoreDetails(Box<CoreDetails>),
     PeopleSearch(PeopleSearchResponse),
+    ProgressUpdateCache(EmptyCacheValue),
     MetadataSearch(MetadataSearchResponse),
     ListennotesSettings(ListennotesSettings),
+    MetadataRecentlyConsumed(EmptyCacheValue),
     UserAnalyticsParameters(ApplicationDateRange),
-    UserRecommendationsKey(UserRecommendationsKey),
     UserCollectionsList(UserCollectionsListResponse),
     MetadataGroupSearch(MetadataGroupSearchResponse),
+    YoutubeMusicSongListened(YoutubeMusicSongListenedResponse),
+    UserMetadataRecommendations(UserMetadataRecommendationsResponse),
 }
diff --git a/crates/models/enum/src/lib.rs b/crates/models/enum/src/lib.rs
index 3930d56f18..e5d65be123 100644
--- a/crates/models/enum/src/lib.rs
+++ b/crates/models/enum/src/lib.rs
@@ -7,19 +7,19 @@ use strum::Display;
 
 /// The different types of media that can be stored.
 #[derive(
+    Eq,
+    Enum,
+    Copy,
+    Hash,
     Debug,
     Clone,
-    Copy,
-    PartialEq,
-    Eq,
+    Default,
     EnumIter,
-    DeriveActiveEnum,
-    Deserialize,
+    PartialEq,
     Serialize,
-    Enum,
-    Default,
-    Hash,
     ConfigEnum,
+    Deserialize,
+    DeriveActiveEnum,
 )]
 #[sea_orm(
     rs_type = "String",
@@ -576,6 +576,7 @@ pub enum IntegrationProvider {
     PlexSink,
     PlexYank,
     GenericJson,
+    YoutubeMusic,
     JellyfinPush,
     JellyfinSink,
     Audiobookshelf,
diff --git a/crates/models/specific/Cargo.toml b/crates/models/external/Cargo.toml
similarity index 91%
rename from crates/models/specific/Cargo.toml
rename to crates/models/external/Cargo.toml
index c8ec8b1250..27c0e0286b 100644
--- a/crates/models/specific/Cargo.toml
+++ b/crates/models/external/Cargo.toml
@@ -1,5 +1,5 @@
 [package]
-name = "specific-models"
+name = "external-models"
 version = "0.1.0"
 edition = "2021"
 
diff --git a/crates/models/specific/src/lib.rs b/crates/models/external/src/lib.rs
similarity index 100%
rename from crates/models/specific/src/lib.rs
rename to crates/models/external/src/lib.rs
diff --git a/crates/models/media/src/lib.rs b/crates/models/media/src/lib.rs
index 1bfdb00504..4a160e43a0 100644
--- a/crates/models/media/src/lib.rs
+++ b/crates/models/media/src/lib.rs
@@ -878,6 +878,8 @@ pub struct IntegrationProviderSpecifics {
     pub jellyfin_push_base_url: Option<String>,
     pub jellyfin_push_username: Option<String>,
     pub jellyfin_push_password: Option<String>,
+
+    pub youtube_music_auth_cookie: Option<String>,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize, SimpleObject)]
@@ -1332,16 +1334,16 @@ pub struct MediaConsumedInput {
     pub lot: MediaLot,
 }
 
-#[derive(SimpleObject)]
+#[derive(SimpleObject, Debug)]
 pub struct UserMetadataDetailsEpisodeProgress {
-    pub episode_number: i32,
     pub times_seen: usize,
+    pub episode_number: i32,
 }
 
-#[derive(SimpleObject)]
+#[derive(SimpleObject, Debug)]
 pub struct UserMetadataDetailsShowSeasonProgress {
-    pub season_number: i32,
     pub times_seen: usize,
+    pub season_number: i32,
     pub episodes: Vec<UserMetadataDetailsEpisodeProgress>,
 }
 
diff --git a/crates/models/user/src/lib.rs b/crates/models/user/src/lib.rs
index 03104723ed..9234671d27 100644
--- a/crates/models/user/src/lib.rs
+++ b/crates/models/user/src/lib.rs
@@ -58,31 +58,13 @@ pub struct UserMediaFeaturesEnabledPreferences {
     #[educe(Default = true)]
     pub enabled: bool,
     #[educe(Default = true)]
-    pub anime: bool,
-    #[educe(Default = true)]
-    pub audio_book: bool,
-    #[educe(Default = true)]
-    pub book: bool,
-    #[educe(Default = true)]
-    pub manga: bool,
-    #[educe(Default = true)]
-    pub music: bool,
-    #[educe(Default = true)]
-    pub movie: bool,
-    #[educe(Default = true)]
-    pub podcast: bool,
-    #[educe(Default = true)]
-    pub show: bool,
-    #[educe(Default = true)]
-    pub video_game: bool,
-    #[educe(Default = true)]
-    pub visual_novel: bool,
-    #[educe(Default = true)]
     pub people: bool,
     #[educe(Default = true)]
     pub groups: bool,
     #[educe(Default = true)]
     pub genres: bool,
+    #[educe(Default = MediaLot::iter().collect())]
+    pub specific: Vec<MediaLot>,
 }
 
 #[derive(
diff --git a/crates/providers/src/igdb.rs b/crates/providers/src/igdb.rs
index 034ebd0e8b..948ce2e897 100644
--- a/crates/providers/src/igdb.rs
+++ b/crates/providers/src/igdb.rs
@@ -10,8 +10,8 @@ use common_models::{
 use common_utils::{ryot_log, PAGE_SIZE};
 use database_models::metadata_group::MetadataGroupWithoutId;
 use dependent_models::{
-    ApplicationCacheValue, IgdbSettings, MetadataGroupSearchResponse, PersonDetails,
-    MetadataPersonRelated, PeopleSearchResponse, SearchResults,
+    ApplicationCacheValue, IgdbSettings, MetadataGroupSearchResponse, MetadataPersonRelated,
+    PeopleSearchResponse, PersonDetails, SearchResults,
 };
 use enum_models::{MediaLot, MediaSource};
 use itertools::Itertools;
@@ -540,14 +540,12 @@ impl IgdbService {
             .get_value::<IgdbSettings>(ApplicationCacheKey::IgdbSettings)
             .await;
         let access_token = if let Some(value) = maybe_settings {
-            value.access_token
+            value
         } else {
             let access_token = self.get_access_token().await;
             cc.set_key(
                 ApplicationCacheKey::IgdbSettings,
-                ApplicationCacheValue::IgdbSettings(IgdbSettings {
-                    access_token: access_token.clone(),
-                }),
+                ApplicationCacheValue::IgdbSettings(access_token.clone()),
             )
             .await
             .ok();
diff --git a/crates/providers/src/listennotes.rs b/crates/providers/src/listennotes.rs
index 72bab1bd6f..c4101d76ca 100644
--- a/crates/providers/src/listennotes.rs
+++ b/crates/providers/src/listennotes.rs
@@ -180,7 +180,7 @@ impl ListennotesService {
             .get_value::<ListennotesSettings>(ApplicationCacheKey::ListennotesSettings)
             .await;
         let genres = if let Some(value) = maybe_settings {
-            value.genres
+            value
         } else {
             #[derive(Debug, Serialize, Deserialize, Default)]
             #[serde(rename_all = "snake_case")]
@@ -205,9 +205,7 @@ impl ListennotesService {
             }
             cc.set_key(
                 ApplicationCacheKey::ListennotesSettings,
-                ApplicationCacheValue::ListennotesSettings(ListennotesSettings {
-                    genres: genres.clone(),
-                }),
+                ApplicationCacheValue::ListennotesSettings(genres.clone()),
             )
             .await
             .ok();
diff --git a/crates/resolvers/miscellaneous/src/lib.rs b/crates/resolvers/miscellaneous/src/lib.rs
index e434d76545..4118390bbc 100644
--- a/crates/resolvers/miscellaneous/src/lib.rs
+++ b/crates/resolvers/miscellaneous/src/lib.rs
@@ -12,11 +12,10 @@ use dependent_models::{
 };
 use media_models::{
     CommitMediaInput, CommitPersonInput, CreateCustomMetadataInput, CreateOrUpdateReviewInput,
-    CreateReviewCommentInput, GenreDetailsInput, GenreListItem, GraphqlCalendarEvent,
-    GraphqlMetadataDetails, GroupedCalendarEvent, MarkEntityAsPartialInput,
-    MetadataGroupsListInput, MetadataListInput, MetadataPartialDetails, PeopleListInput,
-    ProgressUpdateInput, UpdateCustomMetadataInput, UpdateSeenItemInput, UserCalendarEventInput,
-    UserUpcomingCalendarEventInput,
+    CreateReviewCommentInput, GenreDetailsInput, GraphqlCalendarEvent, GraphqlMetadataDetails,
+    GroupedCalendarEvent, MarkEntityAsPartialInput, MetadataGroupsListInput, MetadataListInput,
+    MetadataPartialDetails, PeopleListInput, ProgressUpdateInput, UpdateCustomMetadataInput,
+    UpdateSeenItemInput, UserCalendarEventInput, UserUpcomingCalendarEventInput,
 };
 use miscellaneous_service::MiscellaneousService;
 use traits::AuthProvider;
@@ -111,7 +110,7 @@ impl MiscellaneousQuery {
         &self,
         gql_ctx: &Context<'_>,
         input: SearchInput,
-    ) -> Result<SearchResults<GenreListItem>> {
+    ) -> Result<SearchResults<String>> {
         let service = gql_ctx.data_unchecked::<Arc<MiscellaneousService>>();
         service.genres_list(input).await
     }
diff --git a/crates/resolvers/user/src/lib.rs b/crates/resolvers/user/src/lib.rs
index 6bd927502e..752a7b4018 100644
--- a/crates/resolvers/user/src/lib.rs
+++ b/crates/resolvers/user/src/lib.rs
@@ -3,7 +3,7 @@ use std::sync::Arc;
 use async_graphql::{Context, Object, Result};
 use common_models::StringIdObject;
 use database_models::{access_link, integration, notification_platform, user};
-use dependent_models::UserDetailsResult;
+use dependent_models::{UserDetailsResult, UserMetadataRecommendationsResponse};
 use media_models::{
     AuthUserInput, CreateAccessLinkInput, CreateUserIntegrationInput,
     CreateUserNotificationPlatformInput, LoginResult, OidcTokenOutput, ProcessAccessLinkInput,
@@ -21,11 +21,14 @@ impl AuthProvider for UserQuery {}
 
 #[Object]
 impl UserQuery {
-    /// Get media recommendations for the currently logged in user.
-    async fn user_recommendations(&self, gql_ctx: &Context<'_>) -> Result<Vec<String>> {
+    /// Get metadata recommendations for the currently logged in user.
+    async fn user_metadata_recommendations(
+        &self,
+        gql_ctx: &Context<'_>,
+    ) -> Result<UserMetadataRecommendationsResponse> {
         let service = gql_ctx.data_unchecked::<Arc<UserService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
-        service.user_recommendations(&user_id).await
+        service.user_metadata_recommendations(&user_id).await
     }
 
     /// Get all access links generated by the currently logged in user.
@@ -269,10 +272,12 @@ impl UserMutation {
         service.generate_auth_token(user_id).await
     }
 
-    /// Refresh the user recommendations key.
-    async fn refresh_user_recommendations_key(&self, gql_ctx: &Context<'_>) -> Result<bool> {
+    /// Refresh the user metadata recommendations.
+    async fn refresh_user_metadata_recommendations(&self, gql_ctx: &Context<'_>) -> Result<bool> {
         let service = gql_ctx.data_unchecked::<Arc<UserService>>();
         let user_id = self.user_id_from_ctx(gql_ctx).await?;
-        service.refresh_user_recommendations_key(&user_id).await
+        service
+            .refresh_user_metadata_recommendations(&user_id)
+            .await
     }
 }
diff --git a/crates/services/cache/Cargo.toml b/crates/services/cache/Cargo.toml
index 3aefddbcec..9293690cfb 100644
--- a/crates/services/cache/Cargo.toml
+++ b/crates/services/cache/Cargo.toml
@@ -12,12 +12,12 @@ config = { path = "../../config" }
 database-models = { path = "../../models/database" }
 dependent-models = { path = "../../models/dependent" }
 env-utils = { path = "../../utils/env" }
+itertools = { workspace = true }
 sea-orm = { workspace = true }
 sea-query = { workspace = true }
 serde = { workspace = true }
 serde_json = { workspace = true }
 tracing = { workspace = true }
-uuid = { workspace = true }
 
 [package.metadata.cargo-machete]
-ignored = ["serde", "tracing"]
+ignored = ["tracing"]
diff --git a/crates/services/cache/src/lib.rs b/crates/services/cache/src/lib.rs
index 082266bfa8..ff53032d3a 100644
--- a/crates/services/cache/src/lib.rs
+++ b/crates/services/cache/src/lib.rs
@@ -1,4 +1,4 @@
-use std::sync::Arc;
+use std::{collections::HashMap, sync::Arc};
 
 use async_graphql::Result;
 use chrono::{Duration, Utc};
@@ -7,10 +7,10 @@ use common_utils::ryot_log;
 use database_models::{application_cache, prelude::ApplicationCache};
 use dependent_models::ApplicationCacheValue;
 use env_utils::APP_COMMIT_SHA;
+use itertools::Itertools;
 use sea_orm::{ActiveValue, ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter};
 use sea_query::OnConflict;
 use serde::de::DeserializeOwned;
-use uuid::Uuid;
 
 pub struct CacheService {
     db: DatabaseConnection,
@@ -29,16 +29,12 @@ impl CacheService {
 impl CacheService {
     fn get_expiry_for_key(&self, key: &ApplicationCacheKey) -> i64 {
         match key {
-            ApplicationCacheKey::IgdbSettings
-            | ApplicationCacheKey::ListennotesSettings
-            | ApplicationCacheKey::TmdbSettings => 120,
-
             ApplicationCacheKey::CoreDetails
             | ApplicationCacheKey::PeopleSearch { .. }
             | ApplicationCacheKey::MetadataSearch { .. }
             | ApplicationCacheKey::MetadataGroupSearch { .. }
-            | ApplicationCacheKey::UserRecommendationsKey { .. }
-            | ApplicationCacheKey::MetadataRecentlyConsumed { .. } => 1,
+            | ApplicationCacheKey::MetadataRecentlyConsumed { .. }
+            | ApplicationCacheKey::UserMetadataRecommendations { .. } => 1,
 
             ApplicationCacheKey::UserAnalytics { .. } => 2,
 
@@ -48,25 +44,41 @@ impl CacheService {
             ApplicationCacheKey::ProgressUpdateCache { .. } => {
                 self.config.server.progress_update_threshold
             }
+
+            ApplicationCacheKey::YoutubeMusicSongListened { .. } => 24,
+
+            ApplicationCacheKey::IgdbSettings
+            | ApplicationCacheKey::TmdbSettings
+            | ApplicationCacheKey::ListennotesSettings => 120,
         }
     }
 
-    pub async fn set_key(
+    fn should_respect_version(&self, key: &ApplicationCacheKey) -> bool {
+        match key {
+            ApplicationCacheKey::CoreDetails => true,
+            _ => false,
+        }
+    }
+
+    pub async fn set_keys(
         &self,
-        key: ApplicationCacheKey,
-        value: ApplicationCacheValue,
-    ) -> Result<Uuid> {
+        items: Vec<(ApplicationCacheKey, ApplicationCacheValue)>,
+    ) -> Result<()> {
         let now = Utc::now();
-        let expiry_hours = self.get_expiry_for_key(&key);
-        let to_insert = application_cache::ActiveModel {
-            key: ActiveValue::Set(key),
-            created_at: ActiveValue::Set(now),
-            version: ActiveValue::Set(APP_COMMIT_SHA.to_owned()),
-            value: ActiveValue::Set(serde_json::to_value(value)?),
-            expires_at: ActiveValue::Set(Some(now + Duration::hours(expiry_hours))),
-            ..Default::default()
-        };
-        let inserted = ApplicationCache::insert(to_insert)
+        let to_insert = items
+            .into_iter()
+            .map(|(key, value)| application_cache::ActiveModel {
+                created_at: ActiveValue::Set(now),
+                key: ActiveValue::Set(key.clone()),
+                version: ActiveValue::Set(APP_COMMIT_SHA.to_owned()),
+                value: ActiveValue::Set(serde_json::to_value(value).unwrap()),
+                expires_at: ActiveValue::Set(Some(
+                    now + Duration::hours(self.get_expiry_for_key(&key)),
+                )),
+                ..Default::default()
+            })
+            .collect_vec();
+        let inserted = ApplicationCache::insert_many(to_insert)
             .on_conflict(
                 OnConflict::column(application_cache::Column::Key)
                     .update_columns([
@@ -81,24 +93,46 @@ impl CacheService {
             .await?;
         let insert_id = inserted.last_insert_id;
         ryot_log!(debug, "Inserted application cache with id = {insert_id:?}");
-        Ok(insert_id)
+        Ok(())
+    }
+
+    pub async fn set_key(
+        &self,
+        key: ApplicationCacheKey,
+        value: ApplicationCacheValue,
+    ) -> Result<()> {
+        self.set_keys(vec![(key, value)]).await
+    }
+
+    pub async fn get_values(
+        &self,
+        keys: Vec<ApplicationCacheKey>,
+    ) -> Result<HashMap<ApplicationCacheKey, ApplicationCacheValue>> {
+        let caches = ApplicationCache::find()
+            .filter(application_cache::Column::Key.is_in(keys))
+            .all(&self.db)
+            .await?;
+        let mut values = HashMap::new();
+        for cache in caches {
+            let valid_by_expiry = cache.expires_at.map_or(true, |ea| ea > Utc::now());
+            if !valid_by_expiry {
+                continue;
+            }
+            let should_respect_version = self.should_respect_version(&cache.key);
+            if should_respect_version && cache.version != APP_COMMIT_SHA {
+                continue;
+            }
+            values.insert(cache.key, serde_json::from_value(cache.value)?);
+        }
+        Ok(values)
     }
 
     pub async fn get_value<T: DeserializeOwned>(&self, key: ApplicationCacheKey) -> Option<T> {
-        let cache = ApplicationCache::find()
-            .filter(application_cache::Column::Key.eq(key.clone()))
-            .one(&self.db)
-            .await
-            .ok()?;
-        let db_value = cache
-            .filter(|cache| {
-                cache
-                    .expires_at
-                    .map_or(true, |expires_at| expires_at > Utc::now())
-                    && cache.version == APP_COMMIT_SHA
-            })
-            .map(|m| m.value)?;
-        serde_json::from_value::<T>(db_value).ok()
+        let caches = self.get_values(vec![key.clone()]).await.ok()?;
+        let db_value = serde_json::to_value(caches.get(&key)?).ok()?;
+        db_value
+            .get(key.to_string())
+            .and_then(|v| serde_json::from_value::<T>(v.to_owned()).ok())
     }
 
     pub async fn expire_key(&self, key: ApplicationCacheKey) -> Result<bool> {
diff --git a/crates/services/exporter/src/lib.rs b/crates/services/exporter/src/lib.rs
index 0f3425e3c5..3618bf5754 100644
--- a/crates/services/exporter/src/lib.rs
+++ b/crates/services/exporter/src/lib.rs
@@ -72,16 +72,16 @@ impl ExporterService {
             .file_storage_service
             .list_objects_at_prefix(format!("exports/{}", user_id))
             .await;
-        for (size, object_key) in objects {
+        for (size, key) in objects {
             let url = self
                 .0
                 .file_storage_service
-                .get_presigned_url(object_key.clone())
+                .get_presigned_url(key.clone())
                 .await;
             let metadata = self
                 .0
                 .file_storage_service
-                .get_object_metadata(object_key)
+                .get_object_metadata(key.clone())
                 .await;
             let started_at = DateTime::parse_from_rfc2822(metadata.get("started_at").unwrap())
                 .unwrap()
@@ -89,13 +89,13 @@ impl ExporterService {
             let ended_at = DateTime::parse_from_rfc2822(metadata.get("ended_at").unwrap())
                 .unwrap()
                 .with_timezone(&Utc);
-            let exp = ExportJob {
+            resp.push(ExportJob {
                 size,
                 url,
+                key,
                 ended_at,
                 started_at,
-            };
-            resp.push(exp);
+            });
         }
         resp.sort_by(|a, b| b.ended_at.cmp(&a.ended_at));
         Ok(resp)
diff --git a/crates/services/importer/Cargo.toml b/crates/services/importer/Cargo.toml
index 2f227b97e1..2c59f76de9 100644
--- a/crates/services/importer/Cargo.toml
+++ b/crates/services/importer/Cargo.toml
@@ -13,15 +13,16 @@ chrono-tz = { workspace = true }
 convert_case = { workspace = true }
 csv = { workspace = true }
 data-encoding = { workspace = true }
-enum_meta = { workspace = true }
-env-utils = { path = "../../utils/env" }
-external-utils = { path = "../../utils/external" }
 common-models = { path = "../../models/common" }
 common-utils = { path = "../../utils/common" }
 database-models = { path = "../../models/database" }
 dependent-models = { path = "../../models/dependent" }
 dependent-utils = { path = "../../utils/dependent" }
+enum_meta = { workspace = true }
 enum-models = { path = "../../models/enum" }
+env-utils = { path = "../../utils/env" }
+external-models = { path = "../../models/external" }
+external-utils = { path = "../../utils/external" }
 fitness-models = { path = "../../models/fitness" }
 flate2 = { workspace = true }
 importer-models = { path = "../../models/importer" }
@@ -38,8 +39,6 @@ serde = { workspace = true }
 serde_json = { workspace = true }
 serde_with = { workspace = true }
 serde-xml-rs = { workspace = true }
-specific-models = { path = "../../models/specific" }
-specific-utils = { path = "../../utils/specific" }
 supporting-service = { path = "../supporting" }
 tracing = { workspace = true }
 traits = { path = "../../traits" }
diff --git a/crates/services/importer/src/audiobookshelf.rs b/crates/services/importer/src/audiobookshelf.rs
index 3433c72cff..cf11f8d5ea 100644
--- a/crates/services/importer/src/audiobookshelf.rs
+++ b/crates/services/importer/src/audiobookshelf.rs
@@ -9,6 +9,8 @@ use data_encoding::BASE64;
 use dependent_models::{ImportCompletedItem, ImportResult};
 use dependent_utils::get_identifier_from_book_isbn;
 use enum_models::{ImportSource, MediaLot, MediaSource};
+use external_models::audiobookshelf as audiobookshelf_models;
+use external_utils::audiobookshelf::get_updated_metadata;
 use media_models::{
     DeployUrlAndKeyImportInput, ImportOrExportMetadataItem, ImportOrExportMetadataItemSeen,
     UniqueMediaIdentifier,
@@ -19,8 +21,6 @@ use reqwest::{
     Client,
 };
 use serde_json::json;
-use specific_models::audiobookshelf as audiobookshelf_models;
-use specific_utils::audiobookshelf::get_updated_metadata;
 use supporting_service::SupportingService;
 
 use super::{ImportFailStep, ImportFailedItem};
diff --git a/crates/services/importer/src/plex.rs b/crates/services/importer/src/plex.rs
index 6878610fb8..a718a5a4b0 100644
--- a/crates/services/importer/src/plex.rs
+++ b/crates/services/importer/src/plex.rs
@@ -3,12 +3,12 @@ use async_graphql::Result;
 use common_utils::ryot_log;
 use dependent_models::{ImportCompletedItem, ImportResult};
 use enum_models::{ImportSource, MediaLot, MediaSource};
+use external_models::plex as plex_models;
 use importer_models::{ImportFailStep, ImportFailedItem};
 use media_models::{
     DeployUrlAndKeyImportInput, ImportOrExportMetadataItem, ImportOrExportMetadataItemSeen,
 };
 use reqwest::header::{HeaderName, HeaderValue, ACCEPT};
-use specific_models::plex as plex_models;
 
 pub async fn import(input: DeployUrlAndKeyImportInput) -> Result<ImportResult> {
     let client = get_base_http_client(Some(vec![
diff --git a/crates/services/integration/Cargo.toml b/crates/services/integration/Cargo.toml
index af7dcdf480..cdba859f9d 100644
--- a/crates/services/integration/Cargo.toml
+++ b/crates/services/integration/Cargo.toml
@@ -16,6 +16,7 @@ dependent-models = { path = "../../models/dependent" }
 dependent-utils = { path = "../../utils/dependent" }
 enum-models = { path = "../../models/enum" }
 eventsource-stream = { workspace = true }
+external-models = { path = "../../models/external" }
 external-utils = { path = "../../utils/external" }
 itertools = { workspace = true }
 media-models = { path = "../../models/media" }
@@ -25,13 +26,12 @@ regex = { workspace = true }
 reqwest = { workspace = true }
 rust_decimal = { workspace = true }
 rust_decimal_macros = { workspace = true }
+rustypipe = { workspace = true }
 sea-orm = { workspace = true }
 sea-query = { workspace = true }
 serde = { workspace = true }
 serde_json = { workspace = true }
 sonarr-api-rs = { workspace = true }
-specific-models = { path = "../../models/specific" }
-specific-utils = { path = "../../utils/specific" }
 supporting-service = { path = "../supporting" }
 tokio = { workspace = true }
 tracing = { workspace = true }
diff --git a/crates/services/integration/src/lib.rs b/crates/services/integration/src/lib.rs
index bc123e9d3b..90be09faee 100644
--- a/crates/services/integration/src/lib.rs
+++ b/crates/services/integration/src/lib.rs
@@ -8,7 +8,7 @@ use database_models::{
     prelude::{CollectionToEntity, Integration, Metadata, Seen, UserToEntity},
     seen, user_to_entity,
 };
-use database_utils::user_by_id;
+use database_utils::{server_key_validation_guard, user_by_id};
 use dependent_models::{ImportCompletedItem, ImportResult};
 use dependent_utils::{commit_metadata, process_import};
 use enum_models::{EntityLot, IntegrationLot, IntegrationProvider, MediaLot};
@@ -28,6 +28,16 @@ mod yank;
 pub struct IntegrationService(pub Arc<SupportingService>);
 
 impl IntegrationService {
+    async fn set_integration_last_triggered_on(
+        &self,
+        integration: &integration::Model,
+    ) -> Result<()> {
+        let mut integration: integration::ActiveModel = integration.clone().into();
+        integration.last_triggered_on = ActiveValue::Set(Some(Utc::now()));
+        integration.update(&self.0.db).await?;
+        Ok(())
+    }
+
     async fn integration_progress_update(
         &self,
         integration: integration::Model,
@@ -47,11 +57,10 @@ impl IntegrationService {
                             );
                             false
                         }
-                        Some(_) => true,
-                        None => false,
+                        _ => true,
                     });
                 metadata.seen_history.iter_mut().for_each(|update| {
-                    update.ended_on = Some(Utc::now().date_naive());
+                    update.ended_on = Some(update.ended_on.unwrap_or(Utc::now().date_naive()));
                     if let Some(progress) = update.progress {
                         if progress > integration.maximum_progress.unwrap() {
                             ryot_log!(
@@ -70,12 +79,8 @@ impl IntegrationService {
         })
         .await
         {
-            Ok(_) => {
-                let mut to_update: integration::ActiveModel = integration.into();
-                to_update.last_triggered_on = ActiveValue::Set(Some(Utc::now()));
-                to_update.update(&self.0.db).await?;
-            }
             Err(err) => ryot_log!(debug, "Error updating progress: {:?}", err),
+            Ok(_) => self.set_integration_last_triggered_on(&integration).await?,
         }
         Ok(())
     }
@@ -160,7 +165,7 @@ impl IntegrationService {
                 if !possible_collection_ids.contains(&cte.collection_id) {
                     continue;
                 }
-                let specifics = integration.provider_specifics.unwrap();
+                let specifics = integration.provider_specifics.clone().unwrap();
                 let metadata = Metadata::find_by_id(&cte.entity_id)
                     .one(&self.0.db)
                     .await?
@@ -172,7 +177,7 @@ impl IntegrationService {
                     _ => Some(metadata.identifier.clone()),
                 };
                 if let Some(entity_id) = maybe_entity_id {
-                    let _push_result = match integration.provider {
+                    let push_result = match integration.provider {
                         IntegrationProvider::Radarr => {
                             push::radarr::push_progress(
                                 specifics.radarr_api_key.unwrap(),
@@ -199,6 +204,9 @@ impl IntegrationService {
                         }
                         _ => unreachable!(),
                     };
+                    if push_result.is_ok() {
+                        self.set_integration_last_triggered_on(&integration).await?;
+                    }
                 }
             }
         }
@@ -222,9 +230,10 @@ impl IntegrationService {
             .all(&self.0.db)
             .await?;
         for integration in integrations {
-            let specifics = integration.provider_specifics.unwrap();
-            match integration.provider {
+            let specifics = integration.provider_specifics.clone().unwrap();
+            let push_result = match integration.provider {
                 IntegrationProvider::JellyfinPush => {
+                    server_key_validation_guard(self.0.is_server_key_validated().await?).await?;
                     push::jellyfin::push_progress(
                         specifics.jellyfin_push_base_url.unwrap(),
                         specifics.jellyfin_push_username.unwrap(),
@@ -233,9 +242,12 @@ impl IntegrationService {
                         &metadata_title,
                         &show_extra_information,
                     )
-                    .await?;
+                    .await
                 }
                 _ => unreachable!(),
+            };
+            if push_result.is_ok() {
+                self.set_integration_last_triggered_on(&integration).await?;
             }
         }
         Ok(())
@@ -288,6 +300,15 @@ impl IntegrationService {
                     )
                     .await
                 }
+                IntegrationProvider::YoutubeMusic => {
+                    server_key_validation_guard(self.0.is_server_key_validated().await?).await?;
+                    yank::youtube_music::yank_progress(
+                        specifics.youtube_music_auth_cookie.unwrap(),
+                        user_id,
+                        &self.0,
+                    )
+                    .await
+                }
                 _ => continue,
             };
             if let Ok(update) = response {
diff --git a/crates/services/integration/src/yank/audiobookshelf.rs b/crates/services/integration/src/yank/audiobookshelf.rs
index 3a50d6bd2d..7d681363dc 100644
--- a/crates/services/integration/src/yank/audiobookshelf.rs
+++ b/crates/services/integration/src/yank/audiobookshelf.rs
@@ -7,6 +7,8 @@ use common_utils::ryot_log;
 use dependent_models::{ImportCompletedItem, ImportResult};
 use dependent_utils::get_identifier_from_book_isbn;
 use enum_models::{MediaLot, MediaSource};
+use external_models::audiobookshelf::{self, LibrariesListResponse, ListResponse};
+use external_utils::audiobookshelf::get_updated_metadata;
 use media_models::{
     ImportOrExportMetadataItem, ImportOrExportMetadataItemSeen, UniqueMediaIdentifier,
 };
@@ -16,8 +18,6 @@ use reqwest::{
     Client,
 };
 use rust_decimal_macros::dec;
-use specific_models::audiobookshelf::{self, LibrariesListResponse, ListResponse};
-use specific_utils::audiobookshelf::get_updated_metadata;
 use supporting_service::SupportingService;
 
 fn get_http_client(access_token: &String) -> Client {
diff --git a/crates/services/integration/src/yank/mod.rs b/crates/services/integration/src/yank/mod.rs
index 4f0af3beb0..dcbcbfd4a6 100644
--- a/crates/services/integration/src/yank/mod.rs
+++ b/crates/services/integration/src/yank/mod.rs
@@ -1,3 +1,4 @@
 pub mod audiobookshelf;
 pub mod komga;
 pub mod plex;
+pub mod youtube_music;
diff --git a/crates/services/integration/src/yank/plex.rs b/crates/services/integration/src/yank/plex.rs
index de995fee9e..5e6be22a74 100644
--- a/crates/services/integration/src/yank/plex.rs
+++ b/crates/services/integration/src/yank/plex.rs
@@ -4,9 +4,9 @@ use common_models::DefaultCollection;
 use common_utils::ryot_log;
 use dependent_models::{ImportCompletedItem, ImportResult};
 use enum_models::{MediaLot, MediaSource};
+use external_models::plex as plex_models;
 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![
diff --git a/crates/services/integration/src/yank/youtube_music.rs b/crates/services/integration/src/yank/youtube_music.rs
new file mode 100644
index 0000000000..6179f9af4a
--- /dev/null
+++ b/crates/services/integration/src/yank/youtube_music.rs
@@ -0,0 +1,100 @@
+use std::{collections::HashMap, sync::Arc};
+
+use anyhow::Result;
+use chrono::{NaiveDate, Utc};
+use common_models::{ApplicationCacheKey, UserLevelCacheKey, YoutubeMusicSongListened};
+use common_utils::TEMP_DIR;
+use dependent_models::{ApplicationCacheValue, ImportCompletedItem, ImportResult};
+use enum_models::{MediaLot, MediaSource};
+use media_models::{ImportOrExportMetadataItem, ImportOrExportMetadataItemSeen};
+use rust_decimal_macros::dec;
+use rustypipe::client::RustyPipe;
+use supporting_service::SupportingService;
+
+// DEV: Youtube music only returns one record regardless of how many time you have listened
+// to it that day. It also does not include what time the song was listened to. So, for
+// each song listened to today, we cache the song id with `is_complete=false` and return
+// `progress=35%`. Next time, if the song is already cached, we change the cache to
+// `is_complete=true` and return `progress=100%`. Thus the music gets marked as complete
+// and gets the correct start and end date marked. When the song appears again that day, we
+// silently ignore it since it is already in the cache as completely synced.
+pub async fn yank_progress(
+    auth_cookie: String,
+    user_id: &String,
+    ss: &Arc<SupportingService>,
+) -> Result<ImportResult> {
+    let date = Utc::now().date_naive();
+    let client = RustyPipe::builder().storage_dir(TEMP_DIR).build().unwrap();
+    client.user_auth_set_cookie(auth_cookie).await?;
+    let music_history = client
+        .query()
+        .authenticated()
+        .music_history()
+        .await
+        .unwrap();
+    let songs_listened_to_today = music_history.items.into_iter().rev().filter_map(|history| {
+        history.playback_date.and_then(|d| {
+            let yt_date =
+                NaiveDate::from_ymd_opt(d.year(), d.month() as u32, d.day().into()).unwrap();
+            match yt_date == date {
+                false => None,
+                true => Some((history.item.id, history.item.name)),
+            }
+        })
+    });
+    let cache_keys = songs_listened_to_today
+        .clone()
+        .map(|(id, _)| {
+            (
+                id.clone(),
+                ApplicationCacheKey::YoutubeMusicSongListened(UserLevelCacheKey {
+                    user_id: user_id.to_owned(),
+                    input: YoutubeMusicSongListened {
+                        id: id.clone(),
+                        listened_on: date,
+                    },
+                }),
+            )
+        })
+        .collect::<HashMap<_, _>>();
+    let items_in_cache = ss
+        .cache_service
+        .get_values(cache_keys.iter().map(|(_id, key)| key.clone()).collect())
+        .await
+        .unwrap_or_default();
+    let mut result = ImportResult::default();
+    let mut items_to_cache = vec![];
+    for (song_id, name) in songs_listened_to_today {
+        let Some(cache_key) = cache_keys.get(&song_id) else {
+            continue;
+        };
+        let (cache_value, progress) = match items_in_cache.get(cache_key) {
+            None => (
+                ApplicationCacheValue::YoutubeMusicSongListened(false),
+                dec!(35),
+            ),
+            Some(ApplicationCacheValue::YoutubeMusicSongListened(is_complete)) if !is_complete => (
+                ApplicationCacheValue::YoutubeMusicSongListened(true),
+                dec!(100),
+            ),
+            _ => continue,
+        };
+        result
+            .completed
+            .push(ImportCompletedItem::Metadata(ImportOrExportMetadataItem {
+                source_id: name,
+                identifier: song_id,
+                lot: MediaLot::Music,
+                seen_history: vec![ImportOrExportMetadataItemSeen {
+                    progress: Some(progress),
+                    provider_watched_on: Some("Youtube Music".to_owned()),
+                    ..Default::default()
+                }],
+                source: MediaSource::YoutubeMusic,
+                ..Default::default()
+            }));
+        items_to_cache.push((cache_key.to_owned(), cache_value));
+    }
+    ss.cache_service.set_keys(items_to_cache).await.ok();
+    Ok(result)
+}
diff --git a/crates/services/miscellaneous/src/lib.rs b/crates/services/miscellaneous/src/lib.rs
index 1c779aace2..1db41f46a4 100644
--- a/crates/services/miscellaneous/src/lib.rs
+++ b/crates/services/miscellaneous/src/lib.rs
@@ -47,12 +47,13 @@ use dependent_models::{
 use dependent_utils::{
     add_entity_to_collection, change_metadata_associations, commit_metadata, commit_metadata_group,
     commit_person, create_notification_for_user, create_partial_metadata, create_user_notification,
-    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_groups_list, metadata_images_as_urls, metadata_list, people_list, post_review,
-    progress_update, refresh_collection_to_entity_association, remove_entity_from_collection,
+    deploy_after_handle_media_seen_tasks, deploy_background_job, deploy_update_metadata_group_job,
+    deploy_update_metadata_job, deploy_update_person_job, first_metadata_image_as_url,
+    get_entity_recently_consumed, get_metadata_provider, get_openlibrary_service,
+    get_tmdb_non_media_service, get_users_and_cte_monitoring_entity, get_users_monitoring_entity,
+    handle_after_media_seen_tasks, is_metadata_finished_by_user, metadata_groups_list,
+    metadata_images_as_urls, metadata_list, people_list, post_review, progress_update,
+    refresh_collection_to_entity_association, remove_entity_from_collection,
     update_metadata_and_notify_users,
 };
 use enum_models::{
@@ -1157,11 +1158,7 @@ ORDER BY RANDOM() LIMIT 10;
             .await
             .unwrap()
             .unwrap();
-        self.0
-            .perform_application_job(ApplicationJob::Mp(MpApplicationJob::UpdatePerson(
-                person.id,
-            )))
-            .await?;
+        deploy_update_person_job(&person.id, &self.0).await?;
         Ok(true)
     }
 
@@ -1174,11 +1171,7 @@ ORDER BY RANDOM() LIMIT 10;
             .await
             .unwrap()
             .unwrap();
-        self.0
-            .perform_application_job(ApplicationJob::Mp(MpApplicationJob::UpdateMetadataGroup(
-                metadata_group.id,
-            )))
-            .await?;
+        deploy_update_metadata_group_job(&metadata_group.id, &self.0).await?;
         Ok(true)
     }
 
@@ -1773,7 +1766,7 @@ ORDER BY RANDOM() LIMIT 10;
         Ok(())
     }
 
-    pub async fn genres_list(&self, input: SearchInput) -> Result<SearchResults<GenreListItem>> {
+    pub async fn genres_list(&self, input: SearchInput) -> Result<SearchResults<String>> {
         let page: u64 = input.page.unwrap_or(1).try_into().unwrap();
         let num_items = "num_items";
         let query = Genre::find()
@@ -1805,7 +1798,7 @@ ORDER BY RANDOM() LIMIT 10;
         } = paginator.num_items_and_pages().await?;
         let mut items = vec![];
         for c in paginator.fetch_page(page - 1).await? {
-            items.push(c);
+            items.push(c.id);
         }
         Ok(SearchResults {
             details: SearchDetails {
@@ -2258,11 +2251,14 @@ ORDER BY RANDOM() LIMIT 10;
         &self,
         person_id: String,
     ) -> Result<Vec<(String, UserNotificationContent)>> {
-        let mut notifications = vec![];
         let person = Person::find_by_id(person_id.clone())
             .one(&self.0.db)
             .await?
             .unwrap();
+        if !person.is_partial.unwrap_or_default() {
+            return Ok(vec![]);
+        }
+        let mut notifications = vec![];
         let provider = self.get_non_metadata_provider(person.source).await?;
         let provider_person = provider
             .person_details(&person.identifier, &person.source_specifics)
@@ -2411,10 +2407,7 @@ ORDER BY RANDOM() LIMIT 10;
     }
 
     pub async fn update_person_and_notify_users(&self, person_id: &String) -> Result<()> {
-        let notifications = self
-            .update_person(person_id.clone())
-            .await
-            .unwrap_or_default();
+        let notifications = self.update_person(person_id.clone()).await?;
         if !notifications.is_empty() {
             let users_to_notify =
                 get_users_and_cte_monitoring_entity(person_id, EntityLot::Person, &self.0.db)
@@ -2443,6 +2436,9 @@ ORDER BY RANDOM() LIMIT 10;
             .one(&self.0.db)
             .await?
             .ok_or(Error::new("Group not found"))?;
+        if !eg.is_partial.unwrap_or_default() {
+            return Ok(());
+        }
         let provider = get_metadata_provider(eg.lot, eg.source, &self.0).await?;
         let (group_details, associated_items) =
             provider.metadata_group_details(&eg.identifier).await?;
diff --git a/crates/services/statistics/src/lib.rs b/crates/services/statistics/src/lib.rs
index 8cf0c9a158..6d858fd4ee 100644
--- a/crates/services/statistics/src/lib.rs
+++ b/crates/services/statistics/src/lib.rs
@@ -198,6 +198,11 @@ impl StatisticsService {
                 daily_user_activity::Column::MovieDuration.sum(),
                 "total_movie_duration",
             )
+            .column_as(daily_user_activity::Column::MusicCount.sum(), "music_count")
+            .column_as(
+                daily_user_activity::Column::MusicDuration.sum(),
+                "total_music_duration",
+            )
             .column_as(daily_user_activity::Column::ShowCount.sum(), "show_count")
             .column_as(
                 daily_user_activity::Column::ShowDuration.sum(),
diff --git a/crates/services/user/Cargo.toml b/crates/services/user/Cargo.toml
index d2347a6521..bb28844518 100644
--- a/crates/services/user/Cargo.toml
+++ b/crates/services/user/Cargo.toml
@@ -22,6 +22,7 @@ media-models = { path = "../../models/media" }
 nanoid = { workspace = true }
 notification-service = { path = "../../services/notification" }
 openidconnect = { workspace = true }
+rand = { workspace = true }
 sea-orm = { workspace = true }
 supporting-service = { path = "../supporting" }
 tracing = { workspace = true }
diff --git a/crates/services/user/src/lib.rs b/crates/services/user/src/lib.rs
index 698b41cb71..5a672baeca 100644
--- a/crates/services/user/src/lib.rs
+++ b/crates/services/user/src/lib.rs
@@ -1,9 +1,9 @@
-use std::sync::Arc;
+use std::{collections::HashSet, sync::Arc};
 
 use application_utils::user_id_from_token;
 use argon2::{Argon2, PasswordHash, PasswordVerifier};
 use async_graphql::{Error, Result};
-use chrono::{Timelike, Utc};
+use chrono::Utc;
 use common_models::{ApplicationCacheKey, DefaultCollection, StringIdObject, UserLevelCacheKey};
 use common_utils::ryot_log;
 use database_models::{
@@ -15,7 +15,9 @@ use database_utils::{
     admin_account_guard, deploy_job_to_calculate_user_activities_and_summary, ilike_sql,
     revoke_access_link, server_key_validation_guard, user_by_id,
 };
-use dependent_models::{ApplicationCacheValue, UserDetailsResult, UserRecommendationsKey};
+use dependent_models::{
+    ApplicationCacheValue, UserDetailsResult, UserMetadataRecommendationsResponse,
+};
 use dependent_utils::create_or_update_collection;
 use enum_meta::Meta;
 use enum_models::{IntegrationLot, IntegrationProvider, NotificationPlatformLot, UserLot};
@@ -36,6 +38,7 @@ use openidconnect::{
     core::CoreResponseType, reqwest::async_http_client, AuthenticationFlow, AuthorizationCode,
     CsrfToken, Nonce, Scope, TokenResponse,
 };
+use rand::seq::{IndexedRandom, SliceRandom};
 use sea_orm::{
     prelude::Expr,
     sea_query::{extension::postgres::PgExpr, Func},
@@ -54,7 +57,22 @@ fn empty_nonce_verifier(_nonce: Option<&Nonce>) -> Result<(), String> {
 pub struct UserService(pub Arc<SupportingService>);
 
 impl UserService {
-    pub async fn user_recommendations(&self, user_id: &String) -> Result<Vec<String>> {
+    pub async fn user_metadata_recommendations(
+        &self,
+        user_id: &String,
+    ) -> Result<UserMetadataRecommendationsResponse> {
+        let cc = &self.0.cache_service;
+        let metadata_recommendations_key =
+            ApplicationCacheKey::UserMetadataRecommendations(UserLevelCacheKey {
+                input: (),
+                user_id: user_id.to_owned(),
+            });
+        if let Some(recommendations) = cc
+            .get_value::<UserMetadataRecommendationsResponse>(metadata_recommendations_key.clone())
+            .await
+        {
+            return Ok(recommendations);
+        };
         let preferences = user_by_id(user_id, &self.0).await?.preferences;
         let limit = preferences
             .general
@@ -62,53 +80,55 @@ impl UserService {
             .into_iter()
             .find(|d| d.section == DashboardElementLot::Recommendations)
             .unwrap()
-            .num_elements;
-        let recommendation_key = match self
-            .0
-            .cache_service
-            .get_value::<UserRecommendationsKey>(ApplicationCacheKey::UserRecommendationsKey(
-                UserLevelCacheKey {
-                    input: (),
-                    user_id: user_id.to_owned(),
-                },
-            ))
-            .await
-        {
-            Some(k) => k.recommendations_key,
-            None => Utc::now().hour().to_string(),
-        };
-        let recs = Metadata::find()
-            .filter(metadata::Column::IsRecommendation.eq(true))
-            .order_by(
-                Expr::expr(Func::md5(
-                    Expr::col(metadata::Column::Title)
-                        .concat(Expr::val(user_id))
-                        .concat(Expr::val(recommendation_key)),
-                )),
-                Order::Desc,
-            )
-            .limit(limit)
-            .all(&self.0.db)
-            .await?
-            .into_iter()
-            .map(|r| r.id)
-            .collect_vec();
-        Ok(recs)
+            .num_elements
+            .ok_or_else(|| Error::new("Dashboard element num elements not found"))?;
+        let enabled = preferences.features_enabled.media.specific;
+        if enabled.is_empty() {
+            return Ok(Vec::new());
+        }
+        let mut recommendations = HashSet::new();
+        loop {
+            if recommendations.len() >= limit.try_into().unwrap() {
+                break;
+            }
+            let selected_lot = enabled.choose(&mut rand::rng()).unwrap();
+            let rec = Metadata::find()
+                .select_only()
+                .column(metadata::Column::Id)
+                .filter(metadata::Column::Lot.eq(*selected_lot))
+                .filter(metadata::Column::IsRecommendation.eq(true))
+                .order_by(
+                    Expr::expr(Func::md5(
+                        Expr::col(metadata::Column::Title).concat(Expr::val(nanoid!(12))),
+                    )),
+                    Order::Desc,
+                )
+                .into_tuple::<String>()
+                .one(&self.0.db)
+                .await?;
+            if let Some(rec) = rec {
+                recommendations.insert(rec);
+            }
+        }
+        let mut recommendations = recommendations.into_iter().collect_vec();
+        recommendations.shuffle(&mut rand::rng());
+        cc.set_key(
+            metadata_recommendations_key,
+            ApplicationCacheValue::UserMetadataRecommendations(recommendations.clone()),
+        )
+        .await?;
+        Ok(recommendations)
     }
 
-    pub async fn refresh_user_recommendations_key(&self, user_id: &String) -> Result<bool> {
-        let key = nanoid!(12);
+    pub async fn refresh_user_metadata_recommendations(&self, user_id: &String) -> Result<bool> {
         self.0
             .cache_service
-            .set_key(
-                ApplicationCacheKey::UserRecommendationsKey(UserLevelCacheKey {
+            .expire_key(ApplicationCacheKey::UserMetadataRecommendations(
+                UserLevelCacheKey {
                     input: (),
                     user_id: user_id.to_owned(),
-                }),
-                ApplicationCacheValue::UserRecommendationsKey(UserRecommendationsKey {
-                    recommendations_key: key,
-                }),
-            )
+                },
+            ))
             .await?;
         Ok(true)
     }
@@ -469,15 +489,22 @@ impl UserService {
         user_id: String,
         input: CreateUserIntegrationInput,
     ) -> Result<StringIdObject> {
+        match input.provider {
+            IntegrationProvider::JellyfinPush | IntegrationProvider::YoutubeMusic => {
+                server_key_validation_guard(self.0.is_server_key_validated().await?).await?;
+            }
+            _ => {}
+        }
         if input.minimum_progress > input.maximum_progress {
             return Err(Error::new(
                 "Minimum progress cannot be greater than maximum progress",
             ));
         }
         let lot = match input.provider {
-            IntegrationProvider::Audiobookshelf
-            | IntegrationProvider::Komga
-            | IntegrationProvider::PlexYank => IntegrationLot::Yank,
+            IntegrationProvider::Komga
+            | IntegrationProvider::PlexYank
+            | IntegrationProvider::YoutubeMusic
+            | IntegrationProvider::Audiobookshelf => IntegrationLot::Yank,
             IntegrationProvider::Radarr
             | IntegrationProvider::Sonarr
             | IntegrationProvider::JellyfinPush => IntegrationLot::Push,
diff --git a/crates/utils/database/src/lib.rs b/crates/utils/database/src/lib.rs
index 5fd4115c4d..4218975ba3 100644
--- a/crates/utils/database/src/lib.rs
+++ b/crates/utils/database/src/lib.rs
@@ -543,7 +543,7 @@ pub async fn calculate_user_activities_and_summary(
             }
         } else if let Some(music_extra) = seen.music_specifics {
             if let Some(runtime) = music_extra.duration {
-                activity.music_duration += runtime;
+                activity.music_duration += runtime / 60;
             }
         } else if let Some(book_extra) = seen.book_specifics {
             if let Some(pages) = book_extra.pages {
@@ -687,6 +687,7 @@ pub async fn calculate_user_activities_and_summary(
             + activity.podcast_count
             + activity.anime_count
             + activity.manga_count
+            + activity.music_count
             + activity.audio_book_count
             + activity.book_count
             + activity.video_game_count
@@ -700,6 +701,7 @@ pub async fn calculate_user_activities_and_summary(
             + activity.podcast_duration
             + activity.movie_duration
             + activity.show_duration
+            + activity.music_duration
             + activity.visual_novel_duration
             + activity.video_game_duration;
         activity.hour_records.sort_by_key(|hr| hr.hour);
diff --git a/crates/utils/dependent/src/lib.rs b/crates/utils/dependent/src/lib.rs
index 21f8430213..4b5687af3c 100644
--- a/crates/utils/dependent/src/lib.rs
+++ b/crates/utils/dependent/src/lib.rs
@@ -373,6 +373,9 @@ pub async fn update_metadata(
         .await
         .unwrap()
         .unwrap();
+    if !metadata.is_partial.unwrap_or_default() {
+        return Ok(vec![]);
+    }
     ryot_log!(debug, "Updating metadata for {:?}", metadata_id);
     Metadata::update_many()
         .filter(metadata::Column::Id.eq(metadata_id))
@@ -830,6 +833,28 @@ pub async fn deploy_update_metadata_job(
     Ok(true)
 }
 
+pub async fn deploy_update_metadata_group_job(
+    metadata_group_id: &String,
+    ss: &Arc<SupportingService>,
+) -> Result<bool> {
+    ss.perform_application_job(ApplicationJob::Mp(MpApplicationJob::UpdateMetadataGroup(
+        metadata_group_id.to_owned(),
+    )))
+    .await?;
+    Ok(true)
+}
+
+pub async fn deploy_update_person_job(
+    person_id: &String,
+    ss: &Arc<SupportingService>,
+) -> Result<bool> {
+    ss.perform_application_job(ApplicationJob::Mp(MpApplicationJob::UpdatePerson(
+        person_id.to_owned(),
+    )))
+    .await?;
+    Ok(true)
+}
+
 pub async fn deploy_background_job(
     user_id: &String,
     job_name: BackgroundJob,
@@ -846,6 +871,10 @@ pub async fn deploy_background_job(
     }
     match job_name {
         BackgroundJob::UpdateAllMetadata => {
+            Metadata::update_many()
+                .col_expr(metadata::Column::IsPartial, Expr::value(true))
+                .exec(&ss.db)
+                .await?;
             let many_metadata = Metadata::find()
                 .select_only()
                 .column(metadata::Column::Id)
@@ -1226,7 +1255,7 @@ pub async fn mark_entity_as_recently_consumed(
                     entity_id: entity_id.to_owned(),
                 },
             }),
-            ApplicationCacheValue::Empty(EmptyCacheValue::default()),
+            ApplicationCacheValue::MetadataRecentlyConsumed(EmptyCacheValue::default()),
         )
         .await?;
     Ok(())
@@ -1273,7 +1302,6 @@ pub async fn progress_update(
             provider_watched_on: input.provider_watched_on.clone(),
         },
     });
-    acquire_lock!(&ss.db, &cache_and_lock_key);
     if respect_cache {
         let in_cache = ss
             .cache_service
@@ -1288,6 +1316,7 @@ pub async fn progress_update(
     }
     ryot_log!(debug, "Input for progress_update = {:?}", input);
 
+    acquire_lock!(&ss.db, &cache_and_lock_key);
     let all_prev_seen = Seen::find()
         .filter(seen::Column::Progress.lt(100))
         .filter(seen::Column::UserId.eq(user_id))
@@ -1480,16 +1509,16 @@ pub async fn progress_update(
             ryot_log!(debug, "Progress update percentage = {:?}", progress);
             let seen_insert = seen::ActiveModel {
                 progress: ActiveValue::Set(progress),
-                user_id: ActiveValue::Set(user_id.to_owned()),
-                metadata_id: ActiveValue::Set(input.metadata_id),
                 started_on: ActiveValue::Set(started_on),
                 finished_on: ActiveValue::Set(finished_on),
+                user_id: ActiveValue::Set(user_id.to_owned()),
                 state: ActiveValue::Set(SeenState::InProgress),
-                provider_watched_on: ActiveValue::Set(input.provider_watched_on),
+                metadata_id: ActiveValue::Set(input.metadata_id),
                 show_extra_information: ActiveValue::Set(show_ei),
-                podcast_extra_information: ActiveValue::Set(podcast_ei),
                 anime_extra_information: ActiveValue::Set(anime_ei),
                 manga_extra_information: ActiveValue::Set(manga_ei),
+                podcast_extra_information: ActiveValue::Set(podcast_ei),
+                provider_watched_on: ActiveValue::Set(input.provider_watched_on),
                 ..Default::default()
             };
             seen_insert.insert(&ss.db).await.unwrap()
@@ -1501,7 +1530,7 @@ pub async fn progress_update(
         ss.cache_service
             .set_key(
                 cache_and_lock_key,
-                ApplicationCacheValue::Empty(EmptyCacheValue::default()),
+                ApplicationCacheValue::ProgressUpdateCache(EmptyCacheValue::default()),
             )
             .await?;
     }
@@ -2306,10 +2335,7 @@ where
                         continue;
                     }
                 };
-                ss.perform_application_job(ApplicationJob::Mp(
-                    MpApplicationJob::UpdateMetadataGroup(db_metadata_group_id.clone()),
-                ))
-                .await?;
+                deploy_update_metadata_group_job(&db_metadata_group_id, ss).await?;
                 for review in metadata_group.reviews.iter() {
                     if let Some(input) = convert_review_into_input(
                         review,
@@ -2362,10 +2388,7 @@ where
                         continue;
                     }
                 };
-                ss.perform_application_job(ApplicationJob::Mp(MpApplicationJob::UpdatePerson(
-                    db_person_id.clone(),
-                )))
-                .await?;
+                deploy_update_person_job(&db_person_id, ss).await?;
                 for review in person.reviews.iter() {
                     if let Some(input) = convert_review_into_input(
                         review,
diff --git a/crates/utils/external/Cargo.toml b/crates/utils/external/Cargo.toml
index b5dbcb8a64..35642e5ca2 100644
--- a/crates/utils/external/Cargo.toml
+++ b/crates/utils/external/Cargo.toml
@@ -6,10 +6,14 @@ edition = "2021"
 [dependencies]
 anyhow = { workspace = true }
 common-utils = { path = "../common" }
+database-models = { path = "../../models/database" }
+dependent-utils = { path = "../dependent" }
+enum-models = { path = "../../models/enum" }
 reqwest = { workspace = true }
 sea-orm = { workspace = true }
 serde = { workspace = true }
 serde_json = { workspace = true }
+supporting-service = { path = "../../services/supporting" }
 tracing = { workspace = true }
 
 [package.metadata.cargo-machete]
diff --git a/crates/utils/external/src/lib.rs b/crates/utils/external/src/lib.rs
index 96ce6c4a4c..171488d5ab 100644
--- a/crates/utils/external/src/lib.rs
+++ b/crates/utils/external/src/lib.rs
@@ -1,11 +1,20 @@
+use std::sync::Arc;
+
 use anyhow::Result;
-use common_utils::{ryot_log, APPLICATION_JSON_HEADER, USER_AGENT_STR};
+use common_utils::{ryot_log, sleep_for_n_seconds, APPLICATION_JSON_HEADER, USER_AGENT_STR};
+use database_models::{metadata, prelude::Metadata};
+use dependent_utils::deploy_update_metadata_job;
+use enum_models::{MediaLot, MediaSource};
 use reqwest::{
     header::{HeaderMap, HeaderValue, ACCEPT, AUTHORIZATION, USER_AGENT},
     Client, ClientBuilder,
 };
-use sea_orm::prelude::DateTimeUtc;
+use sea_orm::{
+    prelude::DateTimeUtc,
+    {ColumnTrait, EntityTrait, QueryFilter},
+};
 use serde::{Deserialize, Serialize};
+use supporting_service::SupportingService;
 
 pub mod jellyfin {
     use super::*;
@@ -107,3 +116,33 @@ pub mod jellyfin {
         Ok((client, user_id))
     }
 }
+
+pub mod audiobookshelf {
+    use super::*;
+
+    pub async fn get_updated_metadata(
+        identifier: &String,
+        ss: &Arc<SupportingService>,
+    ) -> Result<metadata::Model> {
+        async fn get_metadata(
+            identifier: &String,
+            ss: &Arc<SupportingService>,
+        ) -> Result<metadata::Model> {
+            let m = Metadata::find()
+                .filter(metadata::Column::Identifier.eq(identifier))
+                .filter(metadata::Column::Lot.eq(MediaLot::Podcast))
+                .filter(metadata::Column::Source.eq(MediaSource::Itunes))
+                .one(&ss.db)
+                .await?
+                .unwrap();
+            Ok(m)
+        }
+
+        let already = get_metadata(identifier, ss).await?;
+        if already.podcast_specifics.is_none() {
+            deploy_update_metadata_job(&already.id, ss).await.unwrap();
+            sleep_for_n_seconds(3).await;
+        }
+        get_metadata(identifier, ss).await
+    }
+}
diff --git a/crates/utils/specific/Cargo.toml b/crates/utils/specific/Cargo.toml
deleted file mode 100644
index bc7e34fdaa..0000000000
--- a/crates/utils/specific/Cargo.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-[package]
-name = "specific-utils"
-version = "0.1.0"
-edition = "2021"
-
-[dependencies]
-anyhow = { workspace = true }
-common-utils = { path = "../common" }
-database-models = { path = "../../models/database" }
-dependent-utils = { path = "../dependent" }
-enum-models = { path = "../../models/enum" }
-sea-orm = { workspace = true }
-supporting-service = { path = "../../services/supporting" }
diff --git a/crates/utils/specific/src/lib.rs b/crates/utils/specific/src/lib.rs
deleted file mode 100644
index 5c4062d008..0000000000
--- a/crates/utils/specific/src/lib.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-use anyhow::Result;
-use common_utils::sleep_for_n_seconds;
-use database_models::{metadata, prelude::Metadata};
-use dependent_utils::deploy_update_metadata_job;
-use enum_models::{MediaLot, MediaSource};
-use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
-use std::sync::Arc;
-use supporting_service::SupportingService;
-
-pub mod audiobookshelf {
-    use super::*;
-
-    pub async fn get_updated_metadata(
-        identifier: &String,
-        ss: &Arc<SupportingService>,
-    ) -> Result<metadata::Model> {
-        async fn get_metadata(
-            identifier: &String,
-            ss: &Arc<SupportingService>,
-        ) -> Result<metadata::Model> {
-            let m = Metadata::find()
-                .filter(metadata::Column::Identifier.eq(identifier))
-                .filter(metadata::Column::Lot.eq(MediaLot::Podcast))
-                .filter(metadata::Column::Source.eq(MediaSource::Itunes))
-                .one(&ss.db)
-                .await?
-                .unwrap();
-            Ok(m)
-        }
-
-        let already = get_metadata(identifier, ss).await?;
-        if already.podcast_specifics.is_none() {
-            deploy_update_metadata_job(&already.id, ss).await.unwrap();
-            sleep_for_n_seconds(3).await;
-        }
-        get_metadata(identifier, ss).await
-    }
-}
diff --git a/docs/content/images/integrations_youtube-music-export-cookies.png b/docs/content/images/integrations_youtube-music-export-cookies.png
new file mode 100644
index 0000000000..664c04a1de
Binary files /dev/null and b/docs/content/images/integrations_youtube-music-export-cookies.png differ
diff --git a/docs/content/importing.md b/docs/content/importing.md
index 02bba4949a..01a3939511 100644
--- a/docs/content/importing.md
+++ b/docs/content/importing.md
@@ -38,10 +38,9 @@ You can import your watched movies and shows from [Plex](https://plex.tv).
       This will only import media that are already finished. Setup an
       [integration](./integrations.md#plex-sink) if you want to import media in progress.
 
-### Steps
-
-- Obtain a `Plex-Token` as described [here](https://www.plexopedia.com/plex-media-server/general/plex-token/#getcurrentusertoken).
-- Enter the correct details in the inputs.
+1. Obtain a `Plex-Token` as described
+   [here](https://www.plexopedia.com/plex-media-server/general/plex-token/#getcurrentusertoken).
+2. Enter the correct details in the inputs.
 
 ## Trakt
 
@@ -49,20 +48,19 @@ All movies and shows can be imported from [Trakt](https://trakt.tv) along with
 their ratings, history, comments and lists. A few points to note.
 
 - It is necessary to set your account's privacy to public during the
-  duration of the import. The Trakt authentication flow is pretty complicated
-  and I don't think it would be worth implementing.
+  duration of the import.
 - Items that have been "check(ed) in" will not be imported.
 
-### Steps
+---
 
-- Login to your Trakt account and go to the settings page.
-- If your account is set to private, uncheck the box next to it. You can revert
+1. Login to your Trakt account and go to the settings page.
+2. If your account is set to private, uncheck the box next to it. You can revert
   this change once the import is complete.
-- If you have any lists that are private, you need to change them to public.
+1. If you have any lists that are private, you need to change them to public.
   Otherwise they will not be imported.
-- Find your profile slug. This is usually your username. You can find it by
+1. Find your profile slug. This is usually your username. You can find it by
   going to your profile page, and checking the URL.
-- Enter this username in the input.
+1. Enter this username in the input.
 
 ## Audiobookshelf
 
@@ -76,11 +74,9 @@ ITunes ID or ISBN.
     - If you have enabled the option to auto delete podcast episodes, you'll have to
       manually mark them as completed.
 
-### Steps
-
-- Obtain an API token as described in the Audiobookshelf
+1. Obtain an API token as described in the Audiobookshelf
   [authentication](https://api.audiobookshelf.org/#authentication) docs.
-- Enter the correct details in the input.
+2. Enter the correct details in the input.
 
 ## Goodreads
 
@@ -89,12 +85,12 @@ following manner:
 
 - Want To Read -> Watchlist
 
-### Steps
+---
 
-- Login to your Goodreads account and go to the "My Books" section.
-- Click on "Import and export" on the left sidebar.
-- Click on "Export Library" and download the CSV file.
-- Upload this file in the input.
+1. Login to your Goodreads account and go to the "My Books" section.
+2. Click on "Import and export" on the left sidebar.
+3. Click on "Export Library" and download the CSV file.
+4. Upload this file in the input.
 
 ## MediaTracker
 
@@ -105,13 +101,13 @@ the following caveats:
   added to the "In Progress" collection so you can manually fix their progress
   if needed.
 
-### Steps
+---
 
-- Login to your MediaTracker account and click on your name on the top right.
-- Click on the "Application tokens" section.
-- Enter a name and click on "Add token".
-- Copy the token that was just generated.
-- Enter the details in the inputs.
+1. Login to your MediaTracker account and click on your name on the top right.
+2. Click on the "Application tokens" section.
+3. Enter a name and click on "Add token".
+4. Copy the token that was just generated.
+5. Enter the details in the inputs.
 
 ## Generic Json
 
@@ -127,36 +123,30 @@ or from a source that is not supported by Ryot.
 The Watchlist and all movies can be imported from [Movary](https://movary.org)
 along with ratings, history, and comments.
 
-### Steps
-
-- Login to your Movary account and go to the settings page. Go to "Personal data"
+1. Login to your Movary account and go to the settings page. Go to "Personal data"
   under the "Account" section.
-- Export "history.csv", "watchlist.csv" and "ratings.csv".
-- Upload these files in the input.
+2. Export "history.csv", "watchlist.csv" and "ratings.csv".
+3. Upload these files in the input.
 
 ## MyAnimeList
 
 Manga and Anime can be imported from [MyAnimeList](https://myanimelist.net) along with
 ratings, history and progress.
 
-### Steps
-
-- Login to your MyAnimeList account and go to
+1. Login to your MyAnimeList account and go to
   [exports](http://myanimelist.net/panel.php?go=export).
-- Export your anime and manga history.
-- Upload these files in the input.
+2. Export your anime and manga history.
+3. Upload these files in the input.
 
 ## Anilist
 
 Manga and anime can be imported from [Anilist](https://anilist.co) along with ratings,
 history, favorites and custom lists.
 
-### Steps
-
-- Login to your Anilist account and go to your [account
+1. Login to your Anilist account and go to your [account
   settings](https://anilist.co/settings/account).
-- Scroll down to the "GDPR Data Download" section and click on "Download".
-- Upload the JSON file in the input.
+2. Scroll down to the "GDPR Data Download" section and click on "Download".
+3. Upload the JSON file in the input.
 
 ## StoryGraph
 
@@ -166,16 +156,16 @@ translates "Read Status" in the following manner:
 
 - to-read -> Watchlist
 
-### Steps
+---
 
-- Login to your account and click on your profile and go to the "Manage Account"
+1. Login to your account and click on your profile and go to the "Manage Account"
   page.
-- Scroll to the bottom and click on "Export StoryGraph Library" and then
+2. Scroll to the bottom and click on "Export StoryGraph Library" and then
   "Generate export".
-- Once the export is done, you will receive an email. refresh the page above and
+3. Once the export is done, you will receive an email. refresh the page above and
   download the CSV file.
-- Optionally, you can edit the CSV file and manually add the missing ISBN.
-- Upload this file in the input.
+4. Optionally, you can edit the CSV file and manually add the missing ISBN.
+5. Upload this file in the input.
 
 ## Strong App
 
@@ -183,35 +173,29 @@ You can import your completed workouts from [Strong](https://www.strong.app/) ap
 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
-
-- Login to your Strong account on the app and go to the "Settings" page.
-- Scroll down to the "General" section and click on "Export data".
-- Upload the csv file in the input.
+1. Login to your Strong account on the app and go to the "Settings" page.
+2. Scroll down to the "General" section and click on "Export data".
+3. Upload the csv file in the input.
 
 ## Hevy
 
 You can import your workouts from [Hevy](https://www.hevy.com). Exercises will be created
 using the same strategy as the [Strong app](#strong-app) importer.
 
-### Steps
-
-- Login to your Hevy account on the app and go to the "Profile" page.
-- Click on the cog icon on the top right and select "Export & Import Data" under
+1. Login to your Hevy account on the app and go to the "Profile" page.
+2. Click on the cog icon on the top right and select "Export & Import Data" under
   "Preferences".
-- Click on "Export" and then click on the button that says "Export Workouts".
-- Upload the csv file in the input.
+3. Click on "Export" and then click on the button that says "Export Workouts".
+4. Upload the csv file in the input.
 
 ## IMDb
 
 You can import your watchlist from [IMDb](https://www.imdb.com). They will be added to
 the "Watchlist" collection.
 
-### Steps
-
-- Go to your account and select your watchlist.
-- Go the bottom and click on the "Export this list" button.
-- Upload the csv file in the input.
+1. Go to your account and select your watchlist.
+2. Go the bottom and click on the "Export this list" button.
+3. Upload the csv file in the input.
 
 ## IGDb
 
@@ -222,12 +206,12 @@ separately. A few points to note:
 - Importing into the "Completed" collection will set 100% progress for the items.
 - Import into any other collection will just add the items to the collection.
 
-### Steps
+---
 
-- Login to your account and go to your profile. The default activity lists can be exported
+1. Login to your account and go to your profile. The default activity lists can be exported
   from  here. Click on the list you want to export and download it as CSV.
-- For your custom lists, please visit the "My Lists" page.
-- Upload the CSV file and choose the collection you want to import into.
+2. For your custom lists, please visit the "My Lists" page.
+3. Upload the CSV file and choose the collection you want to import into.
 
 ## TV Time
 
diff --git a/docs/content/integrations.md b/docs/content/integrations.md
index 6c23e5a903..afb7aaf105 100644
--- a/docs/content/integrations.md
+++ b/docs/content/integrations.md
@@ -135,26 +135,20 @@ have a valid provider ID (Audible, ITunes or ISBN).
 
 ### Komga
 
-The [Komga](https://komga.org/) integration can sync all media if they
-have a valid metadata provider.
-
-#### Steps
-
-If you use [Komf](https://github.com/Snd-R/komf) or some similar metadata provider these
-urls will be populated automatically. If you don't, you will either need to manually add
-the manga to your collection or you can perform the following steps.
+The [Komga](https://komga.org/) integration can sync all media if they have a valid
+metadata provider. If you use [Komf](https://github.com/Snd-R/komf) or some similar
+metadata provider these urls will be populated automatically. If you don't, you will either
+need to manually add the manga to your collection or you can perform the following steps.
 
 1. Navigate to the manga and open the Edit tab
 3. Navigate to the Links tab
-4. Create a link named `AniList` or `MyAnimeList` providing the respective url (not case-sensitive)
-
-Then perform these steps on Ryot
-
-1. Create an integration and select Komga as the source
-2. Provide your Base URL. It should look something like this `http://komga.acme.com` or
+4. Create a link named `AniList` or `MyAnimeList` providing the respective url (not
+   case-sensitive)
+5. On Ryot, create an integration and select Komga as the source
+6. Provide your Base URL. It should look something like this `https://komga.acme.com` or
    `http://127.0.0.1:25600`
-3. Provide your Username and Password.
-4. Provide your preferred metadata provider. Ryot will attempt the others if the preferred
+7. Provide your Username and Password.
+8. Provide your preferred metadata provider. Ryot will attempt the others if the preferred
    is unavailable and will fallback to title search otherwise.
 
 ### Plex Yank
@@ -170,6 +164,23 @@ want to sync media progress, then take a look at the [Plex Sink](#plex-sink) int
    is sufficient.
 2. Go to your Ryot integration settings and fill in the details.
 
+### Youtube Music
+
+L|Pro Version|https://ryot.io|
+
+The [Youtube Music](https://music.youtube.com) integration syncs all music that you have
+listened to ["Yesterday"](https://music.youtube.com/history). Since Youtube Music does not
+have an official API, this integration is prone to breakage and needs some roundabout steps
+to setup.
+
+1. Install the [Cookie Editor](https://cookie-editor.com) extension in your browser. Make
+   sure you allow the extension to work on incognito windows.
+2. Open a new incognito window in your browser and login to Youtube Music.
+3. Once logged in, open the extension and export the cookies as "Header String". After
+   copying them, close the browser immediately so that they are not invalidated.
+  ![image](./images/integrations_youtube-music-export-cookies.png)
+4. Paste the exported cookies in the input.
+
 ## Push integrations
 
 You can enable the following push integrations:
@@ -190,6 +201,8 @@ Events: `Item added to collection`
 
 ### Jellyfin
 
+L|Pro Version|https://ryot.io|
+
 Events: `Item marked as completed`
 
 1. While creating the integration, you will be asked to provide your Jellyfin username and
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index 2ac4485008..ad69a3f04d 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -38,6 +38,7 @@ plugins:
       include_dir: includes
   - offline
   - search
+  - badges
 
 markdown_extensions:
   - admonition
diff --git a/docs/poetry.lock b/docs/poetry.lock
index 48b960b0b8..7fb0ed9505 100644
--- a/docs/poetry.lock
+++ b/docs/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
 
 [[package]]
 name = "babel"
@@ -346,6 +346,20 @@ mkdocs = ">=1"
 natsort = ">=8.1.0"
 wcmatch = ">=7"
 
+[[package]]
+name = "mkdocs-badges"
+version = "0.4.4"
+description = "Add badges to your MkDocs page"
+optional = false
+python-versions = ">=3.9"
+files = [
+    {file = "mkdocs_badges-0.4.4-py3-none-any.whl", hash = "sha256:f46cbc42b75f63bc64234476982d09323aff3c104abcad1f2c2e0e7bfe57c01c"},
+    {file = "mkdocs_badges-0.4.4.tar.gz", hash = "sha256:15805a074388a0416a1e4c5048b670f204e04531ef3755dacd30e1e613a85d0f"},
+]
+
+[package.dependencies]
+mkdocs = ">=1.5.0"
+
 [[package]]
 name = "mkdocs-macros-plugin"
 version = "1.0.4"
@@ -535,6 +549,7 @@ files = [
     {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
     {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
     {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
+    {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
     {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
     {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
     {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
@@ -799,4 +814,4 @@ bracex = ">=2.1.1"
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.11"
-content-hash = "06c0544f8c839c5d169f731b7fa34bf5373a3aba65f7680cfe36294411b6fe1d"
+content-hash = "e25f9a2358659a76f0a62cf973a9f2edc1e85446a43b58aeb226d2838285d094"
diff --git a/docs/pyproject.toml b/docs/pyproject.toml
index 3c3997dfb7..b334b22045 100644
--- a/docs/pyproject.toml
+++ b/docs/pyproject.toml
@@ -8,6 +8,7 @@ package-mode = false
 
 [tool.poetry.dependencies]
 python = "^3.11"
+mkdocs-badges = "^0.4.4"
 mkdocs-macros-plugin = "1.0.4"
 mkdocs-material = "9.3.1"
 mkdocs-awesome-pages-plugin = "2.9.2"
diff --git a/libs/generated/src/graphql/backend/gql.ts b/libs/generated/src/graphql/backend/gql.ts
index 69fbfbf03c..7b15877050 100644
--- a/libs/generated/src/graphql/backend/gql.ts
+++ b/libs/generated/src/graphql/backend/gql.ts
@@ -14,15 +14,15 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/
  * Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size
  */
 const documents = {
-    "mutation RegisterUser($input: RegisterUserInput!) {\n  registerUser(input: $input) {\n    __typename\n    ... on RegisterError {\n      error\n    }\n    ... on StringIdObject {\n      id\n    }\n  }\n}\n\nmutation LoginUser($input: AuthUserInput!) {\n  loginUser(input: $input) {\n    __typename\n    ... on LoginError {\n      error\n    }\n    ... on LoginResponse {\n      apiKey\n    }\n  }\n}\n\nmutation AddEntityToCollection($input: ChangeCollectionToEntityInput!) {\n  addEntityToCollection(input: $input)\n}\n\nmutation CommitMetadata($input: CommitMediaInput!) {\n  commitMetadata(input: $input) {\n    id\n  }\n}\n\nmutation CommitMetadataGroup($input: CommitMediaInput!) {\n  commitMetadataGroup(input: $input) {\n    id\n  }\n}\n\nmutation CommitPerson($input: CommitPersonInput!) {\n  commitPerson(input: $input) {\n    id\n  }\n}\n\nmutation CreateCustomExercise($input: ExerciseInput!) {\n  createCustomExercise(input: $input)\n}\n\nmutation UpdateCustomExercise($input: UpdateCustomExerciseInput!) {\n  updateCustomExercise(input: $input)\n}\n\nmutation UpdateUserIntegration($input: UpdateUserIntegrationInput!) {\n  updateUserIntegration(input: $input)\n}\n\nmutation CreateCustomMetadata($input: CreateCustomMetadataInput!) {\n  createCustomMetadata(input: $input) {\n    id\n  }\n}\n\nmutation UpdateCustomMetadata($input: UpdateCustomMetadataInput!) {\n  updateCustomMetadata(input: $input)\n}\n\nmutation CreateOrUpdateCollection($input: CreateOrUpdateCollectionInput!) {\n  createOrUpdateCollection(input: $input) {\n    id\n  }\n}\n\nmutation CreateReviewComment($input: CreateReviewCommentInput!) {\n  createReviewComment(input: $input)\n}\n\nmutation CreateUserMeasurement($input: UserMeasurementInput!) {\n  createUserMeasurement(input: $input)\n}\n\nmutation CreateUserNotificationPlatform($input: CreateUserNotificationPlatformInput!) {\n  createUserNotificationPlatform(input: $input)\n}\n\nmutation CreateUserIntegration($input: CreateUserIntegrationInput!) {\n  createUserIntegration(input: $input) {\n    id\n  }\n}\n\nmutation CreateOrUpdateUserWorkout($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkout(input: $input)\n}\n\nmutation CreateOrUpdateUserWorkoutTemplate($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkoutTemplate(input: $input)\n}\n\nmutation DeleteCollection($collectionName: String!) {\n  deleteCollection(collectionName: $collectionName)\n}\n\nmutation DeleteReview($reviewId: String!) {\n  deleteReview(reviewId: $reviewId)\n}\n\nmutation DeleteS3Object($key: String!) {\n  deleteS3Object(key: $key)\n}\n\nmutation DeleteSeenItem($seenId: String!) {\n  deleteSeenItem(seenId: $seenId) {\n    id\n  }\n}\n\nmutation DeleteUser($toDeleteUserId: String!) {\n  deleteUser(toDeleteUserId: $toDeleteUserId)\n}\n\nmutation DeleteUserIntegration($integrationId: String!) {\n  deleteUserIntegration(integrationId: $integrationId)\n}\n\nmutation DeleteUserMeasurement($timestamp: DateTime!) {\n  deleteUserMeasurement(timestamp: $timestamp)\n}\n\nmutation DeleteUserNotificationPlatform($notificationId: String!) {\n  deleteUserNotificationPlatform(notificationId: $notificationId)\n}\n\nmutation DeleteUserWorkout($workoutId: String!) {\n  deleteUserWorkout(workoutId: $workoutId)\n}\n\nmutation DeleteUserWorkoutTemplate($workoutTemplateId: String!) {\n  deleteUserWorkoutTemplate(workoutTemplateId: $workoutTemplateId)\n}\n\nmutation DeployBackgroundJob($jobName: BackgroundJob!) {\n  deployBackgroundJob(jobName: $jobName)\n}\n\nmutation DeployBulkProgressUpdate($input: [ProgressUpdateInput!]!) {\n  deployBulkProgressUpdate(input: $input)\n}\n\nmutation DeployExportJob {\n  deployExportJob\n}\n\nmutation DeployImportJob($input: DeployImportJobInput!) {\n  deployImportJob(input: $input)\n}\n\nmutation DeployUpdateMetadataJob($metadataId: String!) {\n  deployUpdateMetadataJob(metadataId: $metadataId)\n}\n\nmutation DeployUpdatePersonJob($personId: String!) {\n  deployUpdatePersonJob(personId: $personId)\n}\n\nmutation DeployUpdateMetadataGroupJob($metadataGroupId: String!) {\n  deployUpdateMetadataGroupJob(metadataGroupId: $metadataGroupId)\n}\n\nmutation UpdateSeenItem($input: UpdateSeenItemInput!) {\n  updateSeenItem(input: $input)\n}\n\nmutation UpdateUserNotificationPlatform($input: UpdateUserNotificationPlatformInput!) {\n  updateUserNotificationPlatform(input: $input)\n}\n\nmutation UpdateUserWorkoutAttributes($input: UpdateUserWorkoutAttributesInput!) {\n  updateUserWorkoutAttributes(input: $input)\n}\n\nmutation GenerateAuthToken {\n  generateAuthToken\n}\n\nmutation MergeMetadata($mergeFrom: String!, $mergeInto: String!) {\n  mergeMetadata(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation DisassociateMetadata($metadataId: String!) {\n  disassociateMetadata(metadataId: $metadataId)\n}\n\nmutation CreateOrUpdateReview($input: CreateOrUpdateReviewInput!) {\n  createOrUpdateReview(input: $input) {\n    id\n  }\n}\n\nmutation PresignedPutS3Url($input: PresignedPutUrlInput!) {\n  presignedPutS3Url(input: $input) {\n    key\n    uploadUrl\n  }\n}\n\nmutation RemoveEntityFromCollection($input: ChangeCollectionToEntityInput!) {\n  removeEntityFromCollection(input: $input) {\n    id\n  }\n}\n\nmutation TestUserNotificationPlatforms {\n  testUserNotificationPlatforms\n}\n\nmutation UpdateUser($input: UpdateUserInput!) {\n  updateUser(input: $input) {\n    id\n  }\n}\n\nmutation UpdateUserPreference($input: UserPreferencesInput!) {\n  updateUserPreference(input: $input)\n}\n\nmutation CreateAccessLink($input: CreateAccessLinkInput!) {\n  createAccessLink(input: $input) {\n    id\n  }\n}\n\nmutation ProcessAccessLink($input: ProcessAccessLinkInput!) {\n  processAccessLink(input: $input) {\n    __typename\n    ... on ProcessAccessLinkError {\n      error\n    }\n    ... on ProcessAccessLinkResponse {\n      apiKey\n      redirectTo\n      tokenValidForDays\n    }\n  }\n}\n\nmutation RevokeAccessLink($accessLinkId: String!) {\n  revokeAccessLink(accessLinkId: $accessLinkId)\n}\n\nmutation UpdateUserExerciseSettings($input: UpdateUserExerciseSettings!) {\n  updateUserExerciseSettings(input: $input)\n}\n\nmutation MergeExercise($mergeFrom: String!, $mergeInto: String!) {\n  mergeExercise(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation RefreshUserRecommendationsKey {\n  refreshUserRecommendationsKey\n}\n\nmutation MarkEntityAsPartial($input: MarkEntityAsPartialInput!) {\n  markEntityAsPartial(input: $input)\n}": types.RegisterUserDocument,
+    "mutation RegisterUser($input: RegisterUserInput!) {\n  registerUser(input: $input) {\n    __typename\n    ... on RegisterError {\n      error\n    }\n    ... on StringIdObject {\n      id\n    }\n  }\n}\n\nmutation LoginUser($input: AuthUserInput!) {\n  loginUser(input: $input) {\n    __typename\n    ... on LoginError {\n      error\n    }\n    ... on LoginResponse {\n      apiKey\n    }\n  }\n}\n\nmutation AddEntityToCollection($input: ChangeCollectionToEntityInput!) {\n  addEntityToCollection(input: $input)\n}\n\nmutation CommitMetadata($input: CommitMediaInput!) {\n  commitMetadata(input: $input) {\n    id\n  }\n}\n\nmutation CommitMetadataGroup($input: CommitMediaInput!) {\n  commitMetadataGroup(input: $input) {\n    id\n  }\n}\n\nmutation CommitPerson($input: CommitPersonInput!) {\n  commitPerson(input: $input) {\n    id\n  }\n}\n\nmutation CreateCustomExercise($input: ExerciseInput!) {\n  createCustomExercise(input: $input)\n}\n\nmutation UpdateCustomExercise($input: UpdateCustomExerciseInput!) {\n  updateCustomExercise(input: $input)\n}\n\nmutation UpdateUserIntegration($input: UpdateUserIntegrationInput!) {\n  updateUserIntegration(input: $input)\n}\n\nmutation CreateCustomMetadata($input: CreateCustomMetadataInput!) {\n  createCustomMetadata(input: $input) {\n    id\n  }\n}\n\nmutation UpdateCustomMetadata($input: UpdateCustomMetadataInput!) {\n  updateCustomMetadata(input: $input)\n}\n\nmutation CreateOrUpdateCollection($input: CreateOrUpdateCollectionInput!) {\n  createOrUpdateCollection(input: $input) {\n    id\n  }\n}\n\nmutation CreateReviewComment($input: CreateReviewCommentInput!) {\n  createReviewComment(input: $input)\n}\n\nmutation CreateUserMeasurement($input: UserMeasurementInput!) {\n  createUserMeasurement(input: $input)\n}\n\nmutation CreateUserNotificationPlatform($input: CreateUserNotificationPlatformInput!) {\n  createUserNotificationPlatform(input: $input)\n}\n\nmutation CreateUserIntegration($input: CreateUserIntegrationInput!) {\n  createUserIntegration(input: $input) {\n    id\n  }\n}\n\nmutation CreateOrUpdateUserWorkout($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkout(input: $input)\n}\n\nmutation CreateOrUpdateUserWorkoutTemplate($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkoutTemplate(input: $input)\n}\n\nmutation DeleteCollection($collectionName: String!) {\n  deleteCollection(collectionName: $collectionName)\n}\n\nmutation DeleteReview($reviewId: String!) {\n  deleteReview(reviewId: $reviewId)\n}\n\nmutation DeleteS3Object($key: String!) {\n  deleteS3Object(key: $key)\n}\n\nmutation DeleteSeenItem($seenId: String!) {\n  deleteSeenItem(seenId: $seenId) {\n    id\n  }\n}\n\nmutation DeleteUser($toDeleteUserId: String!) {\n  deleteUser(toDeleteUserId: $toDeleteUserId)\n}\n\nmutation DeleteUserIntegration($integrationId: String!) {\n  deleteUserIntegration(integrationId: $integrationId)\n}\n\nmutation DeleteUserMeasurement($timestamp: DateTime!) {\n  deleteUserMeasurement(timestamp: $timestamp)\n}\n\nmutation DeleteUserNotificationPlatform($notificationId: String!) {\n  deleteUserNotificationPlatform(notificationId: $notificationId)\n}\n\nmutation DeleteUserWorkout($workoutId: String!) {\n  deleteUserWorkout(workoutId: $workoutId)\n}\n\nmutation DeleteUserWorkoutTemplate($workoutTemplateId: String!) {\n  deleteUserWorkoutTemplate(workoutTemplateId: $workoutTemplateId)\n}\n\nmutation DeployBackgroundJob($jobName: BackgroundJob!) {\n  deployBackgroundJob(jobName: $jobName)\n}\n\nmutation DeployBulkProgressUpdate($input: [ProgressUpdateInput!]!) {\n  deployBulkProgressUpdate(input: $input)\n}\n\nmutation DeployExportJob {\n  deployExportJob\n}\n\nmutation DeployImportJob($input: DeployImportJobInput!) {\n  deployImportJob(input: $input)\n}\n\nmutation DeployUpdateMetadataJob($metadataId: String!) {\n  deployUpdateMetadataJob(metadataId: $metadataId)\n}\n\nmutation DeployUpdatePersonJob($personId: String!) {\n  deployUpdatePersonJob(personId: $personId)\n}\n\nmutation DeployUpdateMetadataGroupJob($metadataGroupId: String!) {\n  deployUpdateMetadataGroupJob(metadataGroupId: $metadataGroupId)\n}\n\nmutation UpdateSeenItem($input: UpdateSeenItemInput!) {\n  updateSeenItem(input: $input)\n}\n\nmutation UpdateUserNotificationPlatform($input: UpdateUserNotificationPlatformInput!) {\n  updateUserNotificationPlatform(input: $input)\n}\n\nmutation UpdateUserWorkoutAttributes($input: UpdateUserWorkoutAttributesInput!) {\n  updateUserWorkoutAttributes(input: $input)\n}\n\nmutation GenerateAuthToken {\n  generateAuthToken\n}\n\nmutation MergeMetadata($mergeFrom: String!, $mergeInto: String!) {\n  mergeMetadata(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation DisassociateMetadata($metadataId: String!) {\n  disassociateMetadata(metadataId: $metadataId)\n}\n\nmutation CreateOrUpdateReview($input: CreateOrUpdateReviewInput!) {\n  createOrUpdateReview(input: $input) {\n    id\n  }\n}\n\nmutation PresignedPutS3Url($input: PresignedPutUrlInput!) {\n  presignedPutS3Url(input: $input) {\n    key\n    uploadUrl\n  }\n}\n\nmutation RemoveEntityFromCollection($input: ChangeCollectionToEntityInput!) {\n  removeEntityFromCollection(input: $input) {\n    id\n  }\n}\n\nmutation TestUserNotificationPlatforms {\n  testUserNotificationPlatforms\n}\n\nmutation UpdateUser($input: UpdateUserInput!) {\n  updateUser(input: $input) {\n    id\n  }\n}\n\nmutation UpdateUserPreference($input: UserPreferencesInput!) {\n  updateUserPreference(input: $input)\n}\n\nmutation CreateAccessLink($input: CreateAccessLinkInput!) {\n  createAccessLink(input: $input) {\n    id\n  }\n}\n\nmutation ProcessAccessLink($input: ProcessAccessLinkInput!) {\n  processAccessLink(input: $input) {\n    __typename\n    ... on ProcessAccessLinkError {\n      error\n    }\n    ... on ProcessAccessLinkResponse {\n      apiKey\n      redirectTo\n      tokenValidForDays\n    }\n  }\n}\n\nmutation RevokeAccessLink($accessLinkId: String!) {\n  revokeAccessLink(accessLinkId: $accessLinkId)\n}\n\nmutation UpdateUserExerciseSettings($input: UpdateUserExerciseSettings!) {\n  updateUserExerciseSettings(input: $input)\n}\n\nmutation MergeExercise($mergeFrom: String!, $mergeInto: String!) {\n  mergeExercise(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation RefreshUserMetadataRecommendations {\n  refreshUserMetadataRecommendations\n}\n\nmutation MarkEntityAsPartial($input: MarkEntityAsPartialInput!) {\n  markEntityAsPartial(input: $input)\n}": types.RegisterUserDocument,
     "query MetadataDetails($metadataId: String!) {\n  metadataDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    source\n    isNsfw\n    isPartial\n    sourceUrl\n    identifier\n    description\n    suggestions\n    publishYear\n    publishDate\n    providerRating\n    createdByUserId\n    productionStatus\n    originalLanguage\n    genres {\n      id\n      name\n    }\n    group {\n      id\n      name\n      part\n    }\n    assets {\n      images\n      videos {\n        videoId\n        source\n      }\n    }\n    creators {\n      name\n      items {\n        id\n        name\n        image\n        character\n      }\n    }\n    watchProviders {\n      name\n      image\n      languages\n    }\n    animeSpecifics {\n      episodes\n    }\n    audioBookSpecifics {\n      runtime\n    }\n    bookSpecifics {\n      pages\n    }\n    movieSpecifics {\n      runtime\n    }\n    mangaSpecifics {\n      volumes\n      chapters\n    }\n    podcastSpecifics {\n      episodes {\n        id\n        title\n        overview\n        thumbnail\n        number\n        runtime\n        publishDate\n      }\n      totalEpisodes\n    }\n    showSpecifics {\n      totalSeasons\n      totalEpisodes\n      runtime\n      seasons {\n        id\n        seasonNumber\n        name\n        overview\n        backdropImages\n        posterImages\n        episodes {\n          id\n          name\n          posterImages\n          episodeNumber\n          publishDate\n          name\n          overview\n          runtime\n        }\n      }\n    }\n    visualNovelSpecifics {\n      length\n    }\n    videoGameSpecifics {\n      platforms\n    }\n    musicSpecifics {\n      duration\n      viewCount\n      byVariousArtists\n    }\n  }\n}": types.MetadataDetailsDocument,
     "query PersonDetails($personId: String!) {\n  personDetails(personId: $personId) {\n    associatedMetadata {\n      ...PersonDetailsGroupedByRolePart\n    }\n    associatedMetadataGroups {\n      ...PersonDetailsGroupedByRolePart\n    }\n    details {\n      id\n      name\n      place\n      source\n      gender\n      website\n      deathDate\n      birthDate\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}": types.PersonDetailsDocument,
-    "query 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            promptForRestTimer\n            showDetailsWhileEditing\n          }\n          exercises {\n            unitSystem\n            setRestTimers {\n              ...SetRestTimersPart\n            }\n          }\n          measurements {\n            custom {\n              name\n              dataType\n            }\n            inbuilt {\n              weight\n              bodyMassIndex\n              totalBodyWater\n              muscle\n              leanBodyMass\n              bodyFat\n              boneMass\n              visceralFat\n              waistCircumference\n              waistToHeightRatio\n              hipCircumference\n              waistToHipRatio\n              chestCircumference\n              thighCircumference\n              bicepsCircumference\n              neckCircumference\n              bodyFatCaliper\n              chestSkinfold\n              abdominalSkinfold\n              thighSkinfold\n              basalMetabolicRate\n              totalDailyEnergyExpenditure\n              calories\n            }\n          }\n        }\n        notifications {\n          toSend\n          enabled\n        }\n        featuresEnabled {\n          analytics {\n            enabled\n          }\n          others {\n            calendar\n            collections\n          }\n          fitness {\n            enabled\n            workouts\n            templates\n            measurements\n          }\n          media {\n            enabled\n            show\n            book\n            anime\n            manga\n            music\n            movie\n            groups\n            people\n            genres\n            podcast\n            audioBook\n            videoGame\n            visualNovel\n          }\n        }\n      }\n    }\n  }\n}": types.UserDetailsDocument,
+    "query UserAnalytics($input: UserAnalyticsInput!) {\n  userAnalytics(input: $input) {\n    hours {\n      hour\n      entities {\n        entityId\n        entityLot\n        metadataLot\n      }\n    }\n    activities {\n      groupedBy\n      totalCount\n      totalDuration\n      items {\n        day\n        bookCount\n        showCount\n        totalCount\n        movieCount\n        musicCount\n        animeCount\n        mangaCount\n        workoutCount\n        podcastCount\n        totalDuration\n        totalBookPages\n        audioBookCount\n        videoGameCount\n        totalReviewCount\n        visualNovelCount\n        totalWorkoutReps\n        totalShowDuration\n        totalMovieDuration\n        totalMusicDuration\n        totalMetadataCount\n        totalWorkoutWeight\n        totalPodcastDuration\n        totalWorkoutDistance\n        totalWorkoutRestTime\n        totalWorkoutDuration\n        userMeasurementCount\n        totalVideoGameDuration\n        totalAudioBookDuration\n        totalPersonReviewCount\n        totalVisualNovelDuration\n        totalMetadataReviewCount\n        totalWorkoutPersonalBests\n        totalCollectionReviewCount\n        totalMetadataGroupReviewCount\n      }\n    }\n    fitness {\n      workoutReps\n      workoutCount\n      workoutWeight\n      workoutDistance\n      workoutDuration\n      workoutRestTime\n      measurementCount\n      workoutPersonalBests\n      workoutExercises {\n        count\n        exercise\n      }\n      workoutMuscles {\n        count\n        muscle\n      }\n      workoutEquipments {\n        count\n        equipment\n      }\n    }\n  }\n}": types.UserAnalyticsDocument,
+    "query UserDetails {\n  userDetails {\n    __typename\n    ... on User {\n      id\n      lot\n      name\n      isDisabled\n      oidcIssuerId\n      preferences {\n        general {\n          reviewScale\n          gridPacking\n          displayNsfw\n          disableVideos\n          persistQueries\n          disableReviews\n          disableIntegrations\n          disableWatchProviders\n          disableNavigationAnimation\n          dashboard {\n            hidden\n            section\n            numElements\n            deduplicateMedia\n          }\n          watchProviders {\n            lot\n            values\n          }\n        }\n        fitness {\n          logging {\n            muteSounds\n            promptForRestTimer\n            showDetailsWhileEditing\n          }\n          exercises {\n            unitSystem\n            setRestTimers {\n              ...SetRestTimersPart\n            }\n          }\n          measurements {\n            custom {\n              name\n              dataType\n            }\n            inbuilt {\n              weight\n              bodyMassIndex\n              totalBodyWater\n              muscle\n              leanBodyMass\n              bodyFat\n              boneMass\n              visceralFat\n              waistCircumference\n              waistToHeightRatio\n              hipCircumference\n              waistToHipRatio\n              chestCircumference\n              thighCircumference\n              bicepsCircumference\n              neckCircumference\n              bodyFatCaliper\n              chestSkinfold\n              abdominalSkinfold\n              thighSkinfold\n              basalMetabolicRate\n              totalDailyEnergyExpenditure\n              calories\n            }\n          }\n        }\n        notifications {\n          toSend\n          enabled\n        }\n        featuresEnabled {\n          analytics {\n            enabled\n          }\n          others {\n            calendar\n            collections\n          }\n          fitness {\n            enabled\n            workouts\n            templates\n            measurements\n          }\n          media {\n            enabled\n            groups\n            people\n            genres\n            specific\n          }\n        }\n      }\n    }\n  }\n}": types.UserDetailsDocument,
     "query UserExerciseDetails($exerciseId: String!) {\n  userExerciseDetails(exerciseId: $exerciseId) {\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    history {\n      idx\n      workoutId\n      workoutEndOn\n      bestSet {\n        ...WorkoutSetRecordPart\n      }\n    }\n    details {\n      exerciseId\n      createdOn\n      lastUpdatedOn\n      exerciseNumTimesInteracted\n      exerciseExtraInformation {\n        settings {\n          excludeFromAnalytics\n          setRestTimers {\n            ...SetRestTimersPart\n          }\n        }\n        lifetimeStats {\n          weight\n          reps\n          distance\n          duration\n          personalBestsAchieved\n        }\n        personalBests {\n          lot\n          sets {\n            setIdx\n            workoutId\n            exerciseIdx\n          }\n        }\n      }\n    }\n  }\n}": types.UserExerciseDetailsDocument,
     "query UserMeasurementsList($input: UserMeasurementsListInput!) {\n  userMeasurementsList(input: $input) {\n    timestamp\n    name\n    comment\n    stats {\n      weight\n      bodyMassIndex\n      totalBodyWater\n      muscle\n      leanBodyMass\n      bodyFat\n      boneMass\n      visceralFat\n      waistCircumference\n      waistToHeightRatio\n      hipCircumference\n      waistToHipRatio\n      chestCircumference\n      thighCircumference\n      bicepsCircumference\n      neckCircumference\n      bodyFatCaliper\n      chestSkinfold\n      abdominalSkinfold\n      thighSkinfold\n      basalMetabolicRate\n      totalDailyEnergyExpenditure\n      calories\n      custom\n    }\n  }\n}": types.UserMeasurementsListDocument,
     "query UserMetadataDetails($metadataId: String!) {\n  userMetadataDetails(metadataId: $metadataId) {\n    mediaReason\n    hasInteracted\n    averageRating\n    seenByAllCount\n    seenByUserCount\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    history {\n      ...SeenPart\n    }\n    nextEntry {\n      season\n      volume\n      episode\n      chapter\n    }\n    inProgress {\n      ...SeenPart\n    }\n    collections {\n      ...CollectionPart\n    }\n    showProgress {\n      timesSeen\n      seasonNumber\n      episodes {\n        episodeNumber\n        timesSeen\n      }\n    }\n    podcastProgress {\n      episodeNumber\n      timesSeen\n    }\n  }\n}": types.UserMetadataDetailsDocument,
-    "query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    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    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    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    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}": types.GetOidcRedirectUrlDocument,
+    "query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    key\n    size\n    endedAt\n    startedAt\n  }\n}\n\nquery UserCollectionsList($name: String) {\n  userCollectionsList(name: $name) {\n    id\n    name\n    count\n    isDefault\n    description\n    creator {\n      id\n      name\n    }\n    collaborators {\n      id\n      name\n    }\n    informationTemplate {\n      lot\n      name\n      required\n      description\n      defaultValue\n    }\n  }\n}\n\nquery UserIntegrations {\n  userIntegrations {\n    id\n    lot\n    provider\n    createdOn\n    isDisabled\n    maximumProgress\n    minimumProgress\n    lastTriggeredOn\n    syncToOwnedCollection\n  }\n}\n\nquery UserNotificationPlatforms {\n  userNotificationPlatforms {\n    id\n    lot\n    createdOn\n    isDisabled\n    description\n  }\n}\n\nquery UsersList($query: String) {\n  usersList(query: $query) {\n    id\n    lot\n    name\n    isDisabled\n  }\n}\n\nquery UserMetadataRecommendations {\n  userMetadataRecommendations\n}\n\nquery UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {\n  userUpcomingCalendarEvents(input: $input) {\n    ...CalendarEventPart\n  }\n}\n\nquery UserCalendarEvents($input: UserCalendarEventInput!) {\n  userCalendarEvents(input: $input) {\n    date\n    events {\n      ...CalendarEventPart\n    }\n  }\n}\n\nquery MetadataPartialDetails($metadataId: String!) {\n  metadataPartialDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    image\n    publishYear\n  }\n}\n\nquery MetadataGroupsList($input: MetadataGroupsListInput!) {\n  metadataGroupsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery PeopleList($input: PeopleListInput!) {\n  peopleList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAccessLinks {\n  userAccessLinks {\n    id\n    name\n    isDemo\n    createdOn\n    expiresOn\n    timesUsed\n    isRevoked\n    maximumUses\n    isAccountDefault\n    isMutationAllowed\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    name\n    level\n    force\n    source\n    muscles\n    mechanic\n    equipment\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery GenreDetails($input: GenreDetailsInput!) {\n  genreDetails(input: $input) {\n    details {\n      id\n      name\n      numItems\n    }\n    contents {\n      details {\n        total\n        nextPage\n      }\n      items\n    }\n  }\n}\n\nquery CollectionContents($input: CollectionContentsInput!) {\n  collectionContents(input: $input) {\n    user {\n      id\n      name\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    results {\n      details {\n        total\n        nextPage\n      }\n      items {\n        entityId\n        entityLot\n      }\n    }\n    details {\n      name\n      description\n      createdOn\n    }\n  }\n}\n\nquery CoreDetails {\n  coreDetails {\n    version\n    docsLink\n    pageSize\n    websiteUrl\n    smtpEnabled\n    oidcEnabled\n    signupAllowed\n    repositoryLink\n    disableTelemetry\n    tokenValidForDays\n    localAuthDisabled\n    fileStorageEnabled\n    isServerKeyValidated\n    metadataLotSourceMappings {\n      lot\n      sources\n    }\n    metadataProviderLanguages {\n      source\n      default\n      supported\n    }\n    frontend {\n      url\n      oidcButtonLabel\n      dashboardMessage\n      umami {\n        domains\n        scriptUrl\n        websiteId\n      }\n    }\n    exerciseParameters {\n      downloadRequired\n      filters {\n        type\n        level\n        force\n        mechanic\n        equipment\n        muscle\n      }\n      lotMapping {\n        lot\n        bests\n      }\n    }\n  }\n}\n\nquery MetadataGroupDetails($metadataGroupId: String!) {\n  metadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    contents\n    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}": types.GetOidcRedirectUrlDocument,
     "fragment SeenPodcastExtraInformationPart on SeenPodcastExtraInformation {\n  episode\n}\n\nfragment SeenShowExtraInformationPart on SeenShowExtraInformation {\n  episode\n  season\n}\n\nfragment SeenAnimeExtraInformationPart on SeenAnimeExtraInformation {\n  episode\n}\n\nfragment SeenMangaExtraInformationPart on SeenMangaExtraInformation {\n  volume\n  chapter\n}\n\nfragment CalendarEventPart on GraphqlCalendarEvent {\n  date\n  metadataId\n  metadataLot\n  episodeName\n  metadataTitle\n  metadataImage\n  calendarEventId\n  showExtraInformation {\n    ...SeenShowExtraInformationPart\n  }\n  podcastExtraInformation {\n    ...SeenPodcastExtraInformationPart\n  }\n  animeExtraInformation {\n    ...SeenAnimeExtraInformationPart\n  }\n}\n\nfragment SeenPart on Seen {\n  id\n  state\n  progress\n  reviewId\n  startedOn\n  finishedOn\n  lastUpdatedOn\n  manualTimeSpent\n  numTimesUpdated\n  providerWatchedOn\n  showExtraInformation {\n    ...SeenShowExtraInformationPart\n  }\n  podcastExtraInformation {\n    ...SeenPodcastExtraInformationPart\n  }\n  animeExtraInformation {\n    ...SeenAnimeExtraInformationPart\n  }\n  mangaExtraInformation {\n    ...SeenMangaExtraInformationPart\n  }\n}\n\nfragment MetadataSearchItemPart on MetadataSearchItem {\n  title\n  image\n  identifier\n  publishYear\n}\n\nfragment WorkoutOrExerciseTotalsPart on WorkoutOrExerciseTotals {\n  reps\n  weight\n  distance\n  duration\n  restTime\n  personalBestsAchieved\n}\n\nfragment EntityAssetsPart on EntityAssets {\n  images\n  videos\n}\n\nfragment WorkoutSetStatisticPart on WorkoutSetStatistic {\n  reps\n  pace\n  oneRm\n  weight\n  volume\n  duration\n  distance\n}\n\nfragment WorkoutSetRecordPart on WorkoutSetRecord {\n  lot\n  personalBests\n  statistic {\n    ...WorkoutSetStatisticPart\n  }\n}\n\nfragment WorkoutSummaryPart on WorkoutSummary {\n  total {\n    ...WorkoutOrExerciseTotalsPart\n  }\n  exercises {\n    id\n    lot\n    numSets\n    bestSet {\n      ...WorkoutSetRecordPart\n    }\n  }\n  focused {\n    lots {\n      lot\n      exercises\n    }\n    levels {\n      level\n      exercises\n    }\n    forces {\n      force\n      exercises\n    }\n    muscles {\n      muscle\n      exercises\n    }\n    equipments {\n      equipment\n      exercises\n    }\n  }\n}\n\nfragment CollectionPart on Collection {\n  id\n  name\n  userId\n}\n\nfragment ReviewItemPart on ReviewItem {\n  id\n  rating\n  postedOn\n  isSpoiler\n  visibility\n  textOriginal\n  textRendered\n  seenItemsAssociatedWith\n  postedBy {\n    id\n    name\n  }\n  comments {\n    id\n    text\n    likedBy\n    createdOn\n    user {\n      id\n      name\n    }\n  }\n  showExtraInformation {\n    season\n    episode\n  }\n  podcastExtraInformation {\n    episode\n  }\n  animeExtraInformation {\n    ...SeenAnimeExtraInformationPart\n  }\n  mangaExtraInformation {\n    ...SeenMangaExtraInformationPart\n  }\n}\n\nfragment WorkoutInformationPart on WorkoutInformation {\n  comment\n  assets {\n    ...EntityAssetsPart\n  }\n  supersets {\n    color\n    exercises\n  }\n  exercises {\n    id\n    lot\n    notes\n    total {\n      ...WorkoutOrExerciseTotalsPart\n    }\n    assets {\n      ...EntityAssetsPart\n    }\n    sets {\n      lot\n      rpe\n      note\n      restTime\n      confirmedAt\n      personalBests\n      statistic {\n        ...WorkoutSetStatisticPart\n      }\n    }\n  }\n}\n\nfragment SetRestTimersPart on SetRestTimersSettings {\n  drop\n  warmup\n  normal\n  failure\n}\n\nfragment PersonDetailsGroupedByRolePart on PersonDetailsGroupedByRole {\n  name\n  count\n  items {\n    entityId\n    character\n  }\n}": types.SeenPodcastExtraInformationPartFragmentDoc,
 };
 
@@ -43,7 +43,7 @@ export function graphql(source: string): unknown;
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
-export function graphql(source: "mutation RegisterUser($input: RegisterUserInput!) {\n  registerUser(input: $input) {\n    __typename\n    ... on RegisterError {\n      error\n    }\n    ... on StringIdObject {\n      id\n    }\n  }\n}\n\nmutation LoginUser($input: AuthUserInput!) {\n  loginUser(input: $input) {\n    __typename\n    ... on LoginError {\n      error\n    }\n    ... on LoginResponse {\n      apiKey\n    }\n  }\n}\n\nmutation AddEntityToCollection($input: ChangeCollectionToEntityInput!) {\n  addEntityToCollection(input: $input)\n}\n\nmutation CommitMetadata($input: CommitMediaInput!) {\n  commitMetadata(input: $input) {\n    id\n  }\n}\n\nmutation CommitMetadataGroup($input: CommitMediaInput!) {\n  commitMetadataGroup(input: $input) {\n    id\n  }\n}\n\nmutation CommitPerson($input: CommitPersonInput!) {\n  commitPerson(input: $input) {\n    id\n  }\n}\n\nmutation CreateCustomExercise($input: ExerciseInput!) {\n  createCustomExercise(input: $input)\n}\n\nmutation UpdateCustomExercise($input: UpdateCustomExerciseInput!) {\n  updateCustomExercise(input: $input)\n}\n\nmutation UpdateUserIntegration($input: UpdateUserIntegrationInput!) {\n  updateUserIntegration(input: $input)\n}\n\nmutation CreateCustomMetadata($input: CreateCustomMetadataInput!) {\n  createCustomMetadata(input: $input) {\n    id\n  }\n}\n\nmutation UpdateCustomMetadata($input: UpdateCustomMetadataInput!) {\n  updateCustomMetadata(input: $input)\n}\n\nmutation CreateOrUpdateCollection($input: CreateOrUpdateCollectionInput!) {\n  createOrUpdateCollection(input: $input) {\n    id\n  }\n}\n\nmutation CreateReviewComment($input: CreateReviewCommentInput!) {\n  createReviewComment(input: $input)\n}\n\nmutation CreateUserMeasurement($input: UserMeasurementInput!) {\n  createUserMeasurement(input: $input)\n}\n\nmutation CreateUserNotificationPlatform($input: CreateUserNotificationPlatformInput!) {\n  createUserNotificationPlatform(input: $input)\n}\n\nmutation CreateUserIntegration($input: CreateUserIntegrationInput!) {\n  createUserIntegration(input: $input) {\n    id\n  }\n}\n\nmutation CreateOrUpdateUserWorkout($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkout(input: $input)\n}\n\nmutation CreateOrUpdateUserWorkoutTemplate($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkoutTemplate(input: $input)\n}\n\nmutation DeleteCollection($collectionName: String!) {\n  deleteCollection(collectionName: $collectionName)\n}\n\nmutation DeleteReview($reviewId: String!) {\n  deleteReview(reviewId: $reviewId)\n}\n\nmutation DeleteS3Object($key: String!) {\n  deleteS3Object(key: $key)\n}\n\nmutation DeleteSeenItem($seenId: String!) {\n  deleteSeenItem(seenId: $seenId) {\n    id\n  }\n}\n\nmutation DeleteUser($toDeleteUserId: String!) {\n  deleteUser(toDeleteUserId: $toDeleteUserId)\n}\n\nmutation DeleteUserIntegration($integrationId: String!) {\n  deleteUserIntegration(integrationId: $integrationId)\n}\n\nmutation DeleteUserMeasurement($timestamp: DateTime!) {\n  deleteUserMeasurement(timestamp: $timestamp)\n}\n\nmutation DeleteUserNotificationPlatform($notificationId: String!) {\n  deleteUserNotificationPlatform(notificationId: $notificationId)\n}\n\nmutation DeleteUserWorkout($workoutId: String!) {\n  deleteUserWorkout(workoutId: $workoutId)\n}\n\nmutation DeleteUserWorkoutTemplate($workoutTemplateId: String!) {\n  deleteUserWorkoutTemplate(workoutTemplateId: $workoutTemplateId)\n}\n\nmutation DeployBackgroundJob($jobName: BackgroundJob!) {\n  deployBackgroundJob(jobName: $jobName)\n}\n\nmutation DeployBulkProgressUpdate($input: [ProgressUpdateInput!]!) {\n  deployBulkProgressUpdate(input: $input)\n}\n\nmutation DeployExportJob {\n  deployExportJob\n}\n\nmutation DeployImportJob($input: DeployImportJobInput!) {\n  deployImportJob(input: $input)\n}\n\nmutation DeployUpdateMetadataJob($metadataId: String!) {\n  deployUpdateMetadataJob(metadataId: $metadataId)\n}\n\nmutation DeployUpdatePersonJob($personId: String!) {\n  deployUpdatePersonJob(personId: $personId)\n}\n\nmutation DeployUpdateMetadataGroupJob($metadataGroupId: String!) {\n  deployUpdateMetadataGroupJob(metadataGroupId: $metadataGroupId)\n}\n\nmutation UpdateSeenItem($input: UpdateSeenItemInput!) {\n  updateSeenItem(input: $input)\n}\n\nmutation UpdateUserNotificationPlatform($input: UpdateUserNotificationPlatformInput!) {\n  updateUserNotificationPlatform(input: $input)\n}\n\nmutation UpdateUserWorkoutAttributes($input: UpdateUserWorkoutAttributesInput!) {\n  updateUserWorkoutAttributes(input: $input)\n}\n\nmutation GenerateAuthToken {\n  generateAuthToken\n}\n\nmutation MergeMetadata($mergeFrom: String!, $mergeInto: String!) {\n  mergeMetadata(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation DisassociateMetadata($metadataId: String!) {\n  disassociateMetadata(metadataId: $metadataId)\n}\n\nmutation CreateOrUpdateReview($input: CreateOrUpdateReviewInput!) {\n  createOrUpdateReview(input: $input) {\n    id\n  }\n}\n\nmutation PresignedPutS3Url($input: PresignedPutUrlInput!) {\n  presignedPutS3Url(input: $input) {\n    key\n    uploadUrl\n  }\n}\n\nmutation RemoveEntityFromCollection($input: ChangeCollectionToEntityInput!) {\n  removeEntityFromCollection(input: $input) {\n    id\n  }\n}\n\nmutation TestUserNotificationPlatforms {\n  testUserNotificationPlatforms\n}\n\nmutation UpdateUser($input: UpdateUserInput!) {\n  updateUser(input: $input) {\n    id\n  }\n}\n\nmutation UpdateUserPreference($input: UserPreferencesInput!) {\n  updateUserPreference(input: $input)\n}\n\nmutation CreateAccessLink($input: CreateAccessLinkInput!) {\n  createAccessLink(input: $input) {\n    id\n  }\n}\n\nmutation ProcessAccessLink($input: ProcessAccessLinkInput!) {\n  processAccessLink(input: $input) {\n    __typename\n    ... on ProcessAccessLinkError {\n      error\n    }\n    ... on ProcessAccessLinkResponse {\n      apiKey\n      redirectTo\n      tokenValidForDays\n    }\n  }\n}\n\nmutation RevokeAccessLink($accessLinkId: String!) {\n  revokeAccessLink(accessLinkId: $accessLinkId)\n}\n\nmutation UpdateUserExerciseSettings($input: UpdateUserExerciseSettings!) {\n  updateUserExerciseSettings(input: $input)\n}\n\nmutation MergeExercise($mergeFrom: String!, $mergeInto: String!) {\n  mergeExercise(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation RefreshUserRecommendationsKey {\n  refreshUserRecommendationsKey\n}\n\nmutation MarkEntityAsPartial($input: MarkEntityAsPartialInput!) {\n  markEntityAsPartial(input: $input)\n}"): (typeof documents)["mutation RegisterUser($input: RegisterUserInput!) {\n  registerUser(input: $input) {\n    __typename\n    ... on RegisterError {\n      error\n    }\n    ... on StringIdObject {\n      id\n    }\n  }\n}\n\nmutation LoginUser($input: AuthUserInput!) {\n  loginUser(input: $input) {\n    __typename\n    ... on LoginError {\n      error\n    }\n    ... on LoginResponse {\n      apiKey\n    }\n  }\n}\n\nmutation AddEntityToCollection($input: ChangeCollectionToEntityInput!) {\n  addEntityToCollection(input: $input)\n}\n\nmutation CommitMetadata($input: CommitMediaInput!) {\n  commitMetadata(input: $input) {\n    id\n  }\n}\n\nmutation CommitMetadataGroup($input: CommitMediaInput!) {\n  commitMetadataGroup(input: $input) {\n    id\n  }\n}\n\nmutation CommitPerson($input: CommitPersonInput!) {\n  commitPerson(input: $input) {\n    id\n  }\n}\n\nmutation CreateCustomExercise($input: ExerciseInput!) {\n  createCustomExercise(input: $input)\n}\n\nmutation UpdateCustomExercise($input: UpdateCustomExerciseInput!) {\n  updateCustomExercise(input: $input)\n}\n\nmutation UpdateUserIntegration($input: UpdateUserIntegrationInput!) {\n  updateUserIntegration(input: $input)\n}\n\nmutation CreateCustomMetadata($input: CreateCustomMetadataInput!) {\n  createCustomMetadata(input: $input) {\n    id\n  }\n}\n\nmutation UpdateCustomMetadata($input: UpdateCustomMetadataInput!) {\n  updateCustomMetadata(input: $input)\n}\n\nmutation CreateOrUpdateCollection($input: CreateOrUpdateCollectionInput!) {\n  createOrUpdateCollection(input: $input) {\n    id\n  }\n}\n\nmutation CreateReviewComment($input: CreateReviewCommentInput!) {\n  createReviewComment(input: $input)\n}\n\nmutation CreateUserMeasurement($input: UserMeasurementInput!) {\n  createUserMeasurement(input: $input)\n}\n\nmutation CreateUserNotificationPlatform($input: CreateUserNotificationPlatformInput!) {\n  createUserNotificationPlatform(input: $input)\n}\n\nmutation CreateUserIntegration($input: CreateUserIntegrationInput!) {\n  createUserIntegration(input: $input) {\n    id\n  }\n}\n\nmutation CreateOrUpdateUserWorkout($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkout(input: $input)\n}\n\nmutation CreateOrUpdateUserWorkoutTemplate($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkoutTemplate(input: $input)\n}\n\nmutation DeleteCollection($collectionName: String!) {\n  deleteCollection(collectionName: $collectionName)\n}\n\nmutation DeleteReview($reviewId: String!) {\n  deleteReview(reviewId: $reviewId)\n}\n\nmutation DeleteS3Object($key: String!) {\n  deleteS3Object(key: $key)\n}\n\nmutation DeleteSeenItem($seenId: String!) {\n  deleteSeenItem(seenId: $seenId) {\n    id\n  }\n}\n\nmutation DeleteUser($toDeleteUserId: String!) {\n  deleteUser(toDeleteUserId: $toDeleteUserId)\n}\n\nmutation DeleteUserIntegration($integrationId: String!) {\n  deleteUserIntegration(integrationId: $integrationId)\n}\n\nmutation DeleteUserMeasurement($timestamp: DateTime!) {\n  deleteUserMeasurement(timestamp: $timestamp)\n}\n\nmutation DeleteUserNotificationPlatform($notificationId: String!) {\n  deleteUserNotificationPlatform(notificationId: $notificationId)\n}\n\nmutation DeleteUserWorkout($workoutId: String!) {\n  deleteUserWorkout(workoutId: $workoutId)\n}\n\nmutation DeleteUserWorkoutTemplate($workoutTemplateId: String!) {\n  deleteUserWorkoutTemplate(workoutTemplateId: $workoutTemplateId)\n}\n\nmutation DeployBackgroundJob($jobName: BackgroundJob!) {\n  deployBackgroundJob(jobName: $jobName)\n}\n\nmutation DeployBulkProgressUpdate($input: [ProgressUpdateInput!]!) {\n  deployBulkProgressUpdate(input: $input)\n}\n\nmutation DeployExportJob {\n  deployExportJob\n}\n\nmutation DeployImportJob($input: DeployImportJobInput!) {\n  deployImportJob(input: $input)\n}\n\nmutation DeployUpdateMetadataJob($metadataId: String!) {\n  deployUpdateMetadataJob(metadataId: $metadataId)\n}\n\nmutation DeployUpdatePersonJob($personId: String!) {\n  deployUpdatePersonJob(personId: $personId)\n}\n\nmutation DeployUpdateMetadataGroupJob($metadataGroupId: String!) {\n  deployUpdateMetadataGroupJob(metadataGroupId: $metadataGroupId)\n}\n\nmutation UpdateSeenItem($input: UpdateSeenItemInput!) {\n  updateSeenItem(input: $input)\n}\n\nmutation UpdateUserNotificationPlatform($input: UpdateUserNotificationPlatformInput!) {\n  updateUserNotificationPlatform(input: $input)\n}\n\nmutation UpdateUserWorkoutAttributes($input: UpdateUserWorkoutAttributesInput!) {\n  updateUserWorkoutAttributes(input: $input)\n}\n\nmutation GenerateAuthToken {\n  generateAuthToken\n}\n\nmutation MergeMetadata($mergeFrom: String!, $mergeInto: String!) {\n  mergeMetadata(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation DisassociateMetadata($metadataId: String!) {\n  disassociateMetadata(metadataId: $metadataId)\n}\n\nmutation CreateOrUpdateReview($input: CreateOrUpdateReviewInput!) {\n  createOrUpdateReview(input: $input) {\n    id\n  }\n}\n\nmutation PresignedPutS3Url($input: PresignedPutUrlInput!) {\n  presignedPutS3Url(input: $input) {\n    key\n    uploadUrl\n  }\n}\n\nmutation RemoveEntityFromCollection($input: ChangeCollectionToEntityInput!) {\n  removeEntityFromCollection(input: $input) {\n    id\n  }\n}\n\nmutation TestUserNotificationPlatforms {\n  testUserNotificationPlatforms\n}\n\nmutation UpdateUser($input: UpdateUserInput!) {\n  updateUser(input: $input) {\n    id\n  }\n}\n\nmutation UpdateUserPreference($input: UserPreferencesInput!) {\n  updateUserPreference(input: $input)\n}\n\nmutation CreateAccessLink($input: CreateAccessLinkInput!) {\n  createAccessLink(input: $input) {\n    id\n  }\n}\n\nmutation ProcessAccessLink($input: ProcessAccessLinkInput!) {\n  processAccessLink(input: $input) {\n    __typename\n    ... on ProcessAccessLinkError {\n      error\n    }\n    ... on ProcessAccessLinkResponse {\n      apiKey\n      redirectTo\n      tokenValidForDays\n    }\n  }\n}\n\nmutation RevokeAccessLink($accessLinkId: String!) {\n  revokeAccessLink(accessLinkId: $accessLinkId)\n}\n\nmutation UpdateUserExerciseSettings($input: UpdateUserExerciseSettings!) {\n  updateUserExerciseSettings(input: $input)\n}\n\nmutation MergeExercise($mergeFrom: String!, $mergeInto: String!) {\n  mergeExercise(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation RefreshUserRecommendationsKey {\n  refreshUserRecommendationsKey\n}\n\nmutation MarkEntityAsPartial($input: MarkEntityAsPartialInput!) {\n  markEntityAsPartial(input: $input)\n}"];
+export function graphql(source: "mutation RegisterUser($input: RegisterUserInput!) {\n  registerUser(input: $input) {\n    __typename\n    ... on RegisterError {\n      error\n    }\n    ... on StringIdObject {\n      id\n    }\n  }\n}\n\nmutation LoginUser($input: AuthUserInput!) {\n  loginUser(input: $input) {\n    __typename\n    ... on LoginError {\n      error\n    }\n    ... on LoginResponse {\n      apiKey\n    }\n  }\n}\n\nmutation AddEntityToCollection($input: ChangeCollectionToEntityInput!) {\n  addEntityToCollection(input: $input)\n}\n\nmutation CommitMetadata($input: CommitMediaInput!) {\n  commitMetadata(input: $input) {\n    id\n  }\n}\n\nmutation CommitMetadataGroup($input: CommitMediaInput!) {\n  commitMetadataGroup(input: $input) {\n    id\n  }\n}\n\nmutation CommitPerson($input: CommitPersonInput!) {\n  commitPerson(input: $input) {\n    id\n  }\n}\n\nmutation CreateCustomExercise($input: ExerciseInput!) {\n  createCustomExercise(input: $input)\n}\n\nmutation UpdateCustomExercise($input: UpdateCustomExerciseInput!) {\n  updateCustomExercise(input: $input)\n}\n\nmutation UpdateUserIntegration($input: UpdateUserIntegrationInput!) {\n  updateUserIntegration(input: $input)\n}\n\nmutation CreateCustomMetadata($input: CreateCustomMetadataInput!) {\n  createCustomMetadata(input: $input) {\n    id\n  }\n}\n\nmutation UpdateCustomMetadata($input: UpdateCustomMetadataInput!) {\n  updateCustomMetadata(input: $input)\n}\n\nmutation CreateOrUpdateCollection($input: CreateOrUpdateCollectionInput!) {\n  createOrUpdateCollection(input: $input) {\n    id\n  }\n}\n\nmutation CreateReviewComment($input: CreateReviewCommentInput!) {\n  createReviewComment(input: $input)\n}\n\nmutation CreateUserMeasurement($input: UserMeasurementInput!) {\n  createUserMeasurement(input: $input)\n}\n\nmutation CreateUserNotificationPlatform($input: CreateUserNotificationPlatformInput!) {\n  createUserNotificationPlatform(input: $input)\n}\n\nmutation CreateUserIntegration($input: CreateUserIntegrationInput!) {\n  createUserIntegration(input: $input) {\n    id\n  }\n}\n\nmutation CreateOrUpdateUserWorkout($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkout(input: $input)\n}\n\nmutation CreateOrUpdateUserWorkoutTemplate($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkoutTemplate(input: $input)\n}\n\nmutation DeleteCollection($collectionName: String!) {\n  deleteCollection(collectionName: $collectionName)\n}\n\nmutation DeleteReview($reviewId: String!) {\n  deleteReview(reviewId: $reviewId)\n}\n\nmutation DeleteS3Object($key: String!) {\n  deleteS3Object(key: $key)\n}\n\nmutation DeleteSeenItem($seenId: String!) {\n  deleteSeenItem(seenId: $seenId) {\n    id\n  }\n}\n\nmutation DeleteUser($toDeleteUserId: String!) {\n  deleteUser(toDeleteUserId: $toDeleteUserId)\n}\n\nmutation DeleteUserIntegration($integrationId: String!) {\n  deleteUserIntegration(integrationId: $integrationId)\n}\n\nmutation DeleteUserMeasurement($timestamp: DateTime!) {\n  deleteUserMeasurement(timestamp: $timestamp)\n}\n\nmutation DeleteUserNotificationPlatform($notificationId: String!) {\n  deleteUserNotificationPlatform(notificationId: $notificationId)\n}\n\nmutation DeleteUserWorkout($workoutId: String!) {\n  deleteUserWorkout(workoutId: $workoutId)\n}\n\nmutation DeleteUserWorkoutTemplate($workoutTemplateId: String!) {\n  deleteUserWorkoutTemplate(workoutTemplateId: $workoutTemplateId)\n}\n\nmutation DeployBackgroundJob($jobName: BackgroundJob!) {\n  deployBackgroundJob(jobName: $jobName)\n}\n\nmutation DeployBulkProgressUpdate($input: [ProgressUpdateInput!]!) {\n  deployBulkProgressUpdate(input: $input)\n}\n\nmutation DeployExportJob {\n  deployExportJob\n}\n\nmutation DeployImportJob($input: DeployImportJobInput!) {\n  deployImportJob(input: $input)\n}\n\nmutation DeployUpdateMetadataJob($metadataId: String!) {\n  deployUpdateMetadataJob(metadataId: $metadataId)\n}\n\nmutation DeployUpdatePersonJob($personId: String!) {\n  deployUpdatePersonJob(personId: $personId)\n}\n\nmutation DeployUpdateMetadataGroupJob($metadataGroupId: String!) {\n  deployUpdateMetadataGroupJob(metadataGroupId: $metadataGroupId)\n}\n\nmutation UpdateSeenItem($input: UpdateSeenItemInput!) {\n  updateSeenItem(input: $input)\n}\n\nmutation UpdateUserNotificationPlatform($input: UpdateUserNotificationPlatformInput!) {\n  updateUserNotificationPlatform(input: $input)\n}\n\nmutation UpdateUserWorkoutAttributes($input: UpdateUserWorkoutAttributesInput!) {\n  updateUserWorkoutAttributes(input: $input)\n}\n\nmutation GenerateAuthToken {\n  generateAuthToken\n}\n\nmutation MergeMetadata($mergeFrom: String!, $mergeInto: String!) {\n  mergeMetadata(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation DisassociateMetadata($metadataId: String!) {\n  disassociateMetadata(metadataId: $metadataId)\n}\n\nmutation CreateOrUpdateReview($input: CreateOrUpdateReviewInput!) {\n  createOrUpdateReview(input: $input) {\n    id\n  }\n}\n\nmutation PresignedPutS3Url($input: PresignedPutUrlInput!) {\n  presignedPutS3Url(input: $input) {\n    key\n    uploadUrl\n  }\n}\n\nmutation RemoveEntityFromCollection($input: ChangeCollectionToEntityInput!) {\n  removeEntityFromCollection(input: $input) {\n    id\n  }\n}\n\nmutation TestUserNotificationPlatforms {\n  testUserNotificationPlatforms\n}\n\nmutation UpdateUser($input: UpdateUserInput!) {\n  updateUser(input: $input) {\n    id\n  }\n}\n\nmutation UpdateUserPreference($input: UserPreferencesInput!) {\n  updateUserPreference(input: $input)\n}\n\nmutation CreateAccessLink($input: CreateAccessLinkInput!) {\n  createAccessLink(input: $input) {\n    id\n  }\n}\n\nmutation ProcessAccessLink($input: ProcessAccessLinkInput!) {\n  processAccessLink(input: $input) {\n    __typename\n    ... on ProcessAccessLinkError {\n      error\n    }\n    ... on ProcessAccessLinkResponse {\n      apiKey\n      redirectTo\n      tokenValidForDays\n    }\n  }\n}\n\nmutation RevokeAccessLink($accessLinkId: String!) {\n  revokeAccessLink(accessLinkId: $accessLinkId)\n}\n\nmutation UpdateUserExerciseSettings($input: UpdateUserExerciseSettings!) {\n  updateUserExerciseSettings(input: $input)\n}\n\nmutation MergeExercise($mergeFrom: String!, $mergeInto: String!) {\n  mergeExercise(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation RefreshUserMetadataRecommendations {\n  refreshUserMetadataRecommendations\n}\n\nmutation MarkEntityAsPartial($input: MarkEntityAsPartialInput!) {\n  markEntityAsPartial(input: $input)\n}"): (typeof documents)["mutation RegisterUser($input: RegisterUserInput!) {\n  registerUser(input: $input) {\n    __typename\n    ... on RegisterError {\n      error\n    }\n    ... on StringIdObject {\n      id\n    }\n  }\n}\n\nmutation LoginUser($input: AuthUserInput!) {\n  loginUser(input: $input) {\n    __typename\n    ... on LoginError {\n      error\n    }\n    ... on LoginResponse {\n      apiKey\n    }\n  }\n}\n\nmutation AddEntityToCollection($input: ChangeCollectionToEntityInput!) {\n  addEntityToCollection(input: $input)\n}\n\nmutation CommitMetadata($input: CommitMediaInput!) {\n  commitMetadata(input: $input) {\n    id\n  }\n}\n\nmutation CommitMetadataGroup($input: CommitMediaInput!) {\n  commitMetadataGroup(input: $input) {\n    id\n  }\n}\n\nmutation CommitPerson($input: CommitPersonInput!) {\n  commitPerson(input: $input) {\n    id\n  }\n}\n\nmutation CreateCustomExercise($input: ExerciseInput!) {\n  createCustomExercise(input: $input)\n}\n\nmutation UpdateCustomExercise($input: UpdateCustomExerciseInput!) {\n  updateCustomExercise(input: $input)\n}\n\nmutation UpdateUserIntegration($input: UpdateUserIntegrationInput!) {\n  updateUserIntegration(input: $input)\n}\n\nmutation CreateCustomMetadata($input: CreateCustomMetadataInput!) {\n  createCustomMetadata(input: $input) {\n    id\n  }\n}\n\nmutation UpdateCustomMetadata($input: UpdateCustomMetadataInput!) {\n  updateCustomMetadata(input: $input)\n}\n\nmutation CreateOrUpdateCollection($input: CreateOrUpdateCollectionInput!) {\n  createOrUpdateCollection(input: $input) {\n    id\n  }\n}\n\nmutation CreateReviewComment($input: CreateReviewCommentInput!) {\n  createReviewComment(input: $input)\n}\n\nmutation CreateUserMeasurement($input: UserMeasurementInput!) {\n  createUserMeasurement(input: $input)\n}\n\nmutation CreateUserNotificationPlatform($input: CreateUserNotificationPlatformInput!) {\n  createUserNotificationPlatform(input: $input)\n}\n\nmutation CreateUserIntegration($input: CreateUserIntegrationInput!) {\n  createUserIntegration(input: $input) {\n    id\n  }\n}\n\nmutation CreateOrUpdateUserWorkout($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkout(input: $input)\n}\n\nmutation CreateOrUpdateUserWorkoutTemplate($input: UserWorkoutInput!) {\n  createOrUpdateUserWorkoutTemplate(input: $input)\n}\n\nmutation DeleteCollection($collectionName: String!) {\n  deleteCollection(collectionName: $collectionName)\n}\n\nmutation DeleteReview($reviewId: String!) {\n  deleteReview(reviewId: $reviewId)\n}\n\nmutation DeleteS3Object($key: String!) {\n  deleteS3Object(key: $key)\n}\n\nmutation DeleteSeenItem($seenId: String!) {\n  deleteSeenItem(seenId: $seenId) {\n    id\n  }\n}\n\nmutation DeleteUser($toDeleteUserId: String!) {\n  deleteUser(toDeleteUserId: $toDeleteUserId)\n}\n\nmutation DeleteUserIntegration($integrationId: String!) {\n  deleteUserIntegration(integrationId: $integrationId)\n}\n\nmutation DeleteUserMeasurement($timestamp: DateTime!) {\n  deleteUserMeasurement(timestamp: $timestamp)\n}\n\nmutation DeleteUserNotificationPlatform($notificationId: String!) {\n  deleteUserNotificationPlatform(notificationId: $notificationId)\n}\n\nmutation DeleteUserWorkout($workoutId: String!) {\n  deleteUserWorkout(workoutId: $workoutId)\n}\n\nmutation DeleteUserWorkoutTemplate($workoutTemplateId: String!) {\n  deleteUserWorkoutTemplate(workoutTemplateId: $workoutTemplateId)\n}\n\nmutation DeployBackgroundJob($jobName: BackgroundJob!) {\n  deployBackgroundJob(jobName: $jobName)\n}\n\nmutation DeployBulkProgressUpdate($input: [ProgressUpdateInput!]!) {\n  deployBulkProgressUpdate(input: $input)\n}\n\nmutation DeployExportJob {\n  deployExportJob\n}\n\nmutation DeployImportJob($input: DeployImportJobInput!) {\n  deployImportJob(input: $input)\n}\n\nmutation DeployUpdateMetadataJob($metadataId: String!) {\n  deployUpdateMetadataJob(metadataId: $metadataId)\n}\n\nmutation DeployUpdatePersonJob($personId: String!) {\n  deployUpdatePersonJob(personId: $personId)\n}\n\nmutation DeployUpdateMetadataGroupJob($metadataGroupId: String!) {\n  deployUpdateMetadataGroupJob(metadataGroupId: $metadataGroupId)\n}\n\nmutation UpdateSeenItem($input: UpdateSeenItemInput!) {\n  updateSeenItem(input: $input)\n}\n\nmutation UpdateUserNotificationPlatform($input: UpdateUserNotificationPlatformInput!) {\n  updateUserNotificationPlatform(input: $input)\n}\n\nmutation UpdateUserWorkoutAttributes($input: UpdateUserWorkoutAttributesInput!) {\n  updateUserWorkoutAttributes(input: $input)\n}\n\nmutation GenerateAuthToken {\n  generateAuthToken\n}\n\nmutation MergeMetadata($mergeFrom: String!, $mergeInto: String!) {\n  mergeMetadata(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation DisassociateMetadata($metadataId: String!) {\n  disassociateMetadata(metadataId: $metadataId)\n}\n\nmutation CreateOrUpdateReview($input: CreateOrUpdateReviewInput!) {\n  createOrUpdateReview(input: $input) {\n    id\n  }\n}\n\nmutation PresignedPutS3Url($input: PresignedPutUrlInput!) {\n  presignedPutS3Url(input: $input) {\n    key\n    uploadUrl\n  }\n}\n\nmutation RemoveEntityFromCollection($input: ChangeCollectionToEntityInput!) {\n  removeEntityFromCollection(input: $input) {\n    id\n  }\n}\n\nmutation TestUserNotificationPlatforms {\n  testUserNotificationPlatforms\n}\n\nmutation UpdateUser($input: UpdateUserInput!) {\n  updateUser(input: $input) {\n    id\n  }\n}\n\nmutation UpdateUserPreference($input: UserPreferencesInput!) {\n  updateUserPreference(input: $input)\n}\n\nmutation CreateAccessLink($input: CreateAccessLinkInput!) {\n  createAccessLink(input: $input) {\n    id\n  }\n}\n\nmutation ProcessAccessLink($input: ProcessAccessLinkInput!) {\n  processAccessLink(input: $input) {\n    __typename\n    ... on ProcessAccessLinkError {\n      error\n    }\n    ... on ProcessAccessLinkResponse {\n      apiKey\n      redirectTo\n      tokenValidForDays\n    }\n  }\n}\n\nmutation RevokeAccessLink($accessLinkId: String!) {\n  revokeAccessLink(accessLinkId: $accessLinkId)\n}\n\nmutation UpdateUserExerciseSettings($input: UpdateUserExerciseSettings!) {\n  updateUserExerciseSettings(input: $input)\n}\n\nmutation MergeExercise($mergeFrom: String!, $mergeInto: String!) {\n  mergeExercise(mergeFrom: $mergeFrom, mergeInto: $mergeInto)\n}\n\nmutation RefreshUserMetadataRecommendations {\n  refreshUserMetadataRecommendations\n}\n\nmutation MarkEntityAsPartial($input: MarkEntityAsPartialInput!) {\n  markEntityAsPartial(input: $input)\n}"];
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
@@ -55,11 +55,11 @@ export function graphql(source: "query PersonDetails($personId: String!) {\n  pe
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
-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}"];
+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        musicCount\n        animeCount\n        mangaCount\n        workoutCount\n        podcastCount\n        totalDuration\n        totalBookPages\n        audioBookCount\n        videoGameCount\n        totalReviewCount\n        visualNovelCount\n        totalWorkoutReps\n        totalShowDuration\n        totalMovieDuration\n        totalMusicDuration\n        totalMetadataCount\n        totalWorkoutWeight\n        totalPodcastDuration\n        totalWorkoutDistance\n        totalWorkoutRestTime\n        totalWorkoutDuration\n        userMeasurementCount\n        totalVideoGameDuration\n        totalAudioBookDuration\n        totalPersonReviewCount\n        totalVisualNovelDuration\n        totalMetadataReviewCount\n        totalWorkoutPersonalBests\n        totalCollectionReviewCount\n        totalMetadataGroupReviewCount\n      }\n    }\n    fitness {\n      workoutReps\n      workoutCount\n      workoutWeight\n      workoutDistance\n      workoutDuration\n      workoutRestTime\n      measurementCount\n      workoutPersonalBests\n      workoutExercises {\n        count\n        exercise\n      }\n      workoutMuscles {\n        count\n        muscle\n      }\n      workoutEquipments {\n        count\n        equipment\n      }\n    }\n  }\n}"): (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        musicCount\n        animeCount\n        mangaCount\n        workoutCount\n        podcastCount\n        totalDuration\n        totalBookPages\n        audioBookCount\n        videoGameCount\n        totalReviewCount\n        visualNovelCount\n        totalWorkoutReps\n        totalShowDuration\n        totalMovieDuration\n        totalMusicDuration\n        totalMetadataCount\n        totalWorkoutWeight\n        totalPodcastDuration\n        totalWorkoutDistance\n        totalWorkoutRestTime\n        totalWorkoutDuration\n        userMeasurementCount\n        totalVideoGameDuration\n        totalAudioBookDuration\n        totalPersonReviewCount\n        totalVisualNovelDuration\n        totalMetadataReviewCount\n        totalWorkoutPersonalBests\n        totalCollectionReviewCount\n        totalMetadataGroupReviewCount\n      }\n    }\n    fitness {\n      workoutReps\n      workoutCount\n      workoutWeight\n      workoutDistance\n      workoutDuration\n      workoutRestTime\n      measurementCount\n      workoutPersonalBests\n      workoutExercises {\n        count\n        exercise\n      }\n      workoutMuscles {\n        count\n        muscle\n      }\n      workoutEquipments {\n        count\n        equipment\n      }\n    }\n  }\n}"];
 /**
  * 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            promptForRestTimer\n            showDetailsWhileEditing\n          }\n          exercises {\n            unitSystem\n            setRestTimers {\n              ...SetRestTimersPart\n            }\n          }\n          measurements {\n            custom {\n              name\n              dataType\n            }\n            inbuilt {\n              weight\n              bodyMassIndex\n              totalBodyWater\n              muscle\n              leanBodyMass\n              bodyFat\n              boneMass\n              visceralFat\n              waistCircumference\n              waistToHeightRatio\n              hipCircumference\n              waistToHipRatio\n              chestCircumference\n              thighCircumference\n              bicepsCircumference\n              neckCircumference\n              bodyFatCaliper\n              chestSkinfold\n              abdominalSkinfold\n              thighSkinfold\n              basalMetabolicRate\n              totalDailyEnergyExpenditure\n              calories\n            }\n          }\n        }\n        notifications {\n          toSend\n          enabled\n        }\n        featuresEnabled {\n          analytics {\n            enabled\n          }\n          others {\n            calendar\n            collections\n          }\n          fitness {\n            enabled\n            workouts\n            templates\n            measurements\n          }\n          media {\n            enabled\n            show\n            book\n            anime\n            manga\n            music\n            movie\n            groups\n            people\n            genres\n            podcast\n            audioBook\n            videoGame\n            visualNovel\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            promptForRestTimer\n            showDetailsWhileEditing\n          }\n          exercises {\n            unitSystem\n            setRestTimers {\n              ...SetRestTimersPart\n            }\n          }\n          measurements {\n            custom {\n              name\n              dataType\n            }\n            inbuilt {\n              weight\n              bodyMassIndex\n              totalBodyWater\n              muscle\n              leanBodyMass\n              bodyFat\n              boneMass\n              visceralFat\n              waistCircumference\n              waistToHeightRatio\n              hipCircumference\n              waistToHipRatio\n              chestCircumference\n              thighCircumference\n              bicepsCircumference\n              neckCircumference\n              bodyFatCaliper\n              chestSkinfold\n              abdominalSkinfold\n              thighSkinfold\n              basalMetabolicRate\n              totalDailyEnergyExpenditure\n              calories\n            }\n          }\n        }\n        notifications {\n          toSend\n          enabled\n        }\n        featuresEnabled {\n          analytics {\n            enabled\n          }\n          others {\n            calendar\n            collections\n          }\n          fitness {\n            enabled\n            workouts\n            templates\n            measurements\n          }\n          media {\n            enabled\n            show\n            book\n            anime\n            manga\n            music\n            movie\n            groups\n            people\n            genres\n            podcast\n            audioBook\n            videoGame\n            visualNovel\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            promptForRestTimer\n            showDetailsWhileEditing\n          }\n          exercises {\n            unitSystem\n            setRestTimers {\n              ...SetRestTimersPart\n            }\n          }\n          measurements {\n            custom {\n              name\n              dataType\n            }\n            inbuilt {\n              weight\n              bodyMassIndex\n              totalBodyWater\n              muscle\n              leanBodyMass\n              bodyFat\n              boneMass\n              visceralFat\n              waistCircumference\n              waistToHeightRatio\n              hipCircumference\n              waistToHipRatio\n              chestCircumference\n              thighCircumference\n              bicepsCircumference\n              neckCircumference\n              bodyFatCaliper\n              chestSkinfold\n              abdominalSkinfold\n              thighSkinfold\n              basalMetabolicRate\n              totalDailyEnergyExpenditure\n              calories\n            }\n          }\n        }\n        notifications {\n          toSend\n          enabled\n        }\n        featuresEnabled {\n          analytics {\n            enabled\n          }\n          others {\n            calendar\n            collections\n          }\n          fitness {\n            enabled\n            workouts\n            templates\n            measurements\n          }\n          media {\n            enabled\n            groups\n            people\n            genres\n            specific\n          }\n        }\n      }\n    }\n  }\n}"): (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            promptForRestTimer\n            showDetailsWhileEditing\n          }\n          exercises {\n            unitSystem\n            setRestTimers {\n              ...SetRestTimersPart\n            }\n          }\n          measurements {\n            custom {\n              name\n              dataType\n            }\n            inbuilt {\n              weight\n              bodyMassIndex\n              totalBodyWater\n              muscle\n              leanBodyMass\n              bodyFat\n              boneMass\n              visceralFat\n              waistCircumference\n              waistToHeightRatio\n              hipCircumference\n              waistToHipRatio\n              chestCircumference\n              thighCircumference\n              bicepsCircumference\n              neckCircumference\n              bodyFatCaliper\n              chestSkinfold\n              abdominalSkinfold\n              thighSkinfold\n              basalMetabolicRate\n              totalDailyEnergyExpenditure\n              calories\n            }\n          }\n        }\n        notifications {\n          toSend\n          enabled\n        }\n        featuresEnabled {\n          analytics {\n            enabled\n          }\n          others {\n            calendar\n            collections\n          }\n          fitness {\n            enabled\n            workouts\n            templates\n            measurements\n          }\n          media {\n            enabled\n            groups\n            people\n            genres\n            specific\n          }\n        }\n      }\n    }\n  }\n}"];
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
@@ -75,7 +75,7 @@ export function graphql(source: "query UserMetadataDetails($metadataId: String!)
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
-export function graphql(source: "query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    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    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    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    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}"): (typeof documents)["query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    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    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    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    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}"];
+export function graphql(source: "query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    key\n    size\n    endedAt\n    startedAt\n  }\n}\n\nquery UserCollectionsList($name: String) {\n  userCollectionsList(name: $name) {\n    id\n    name\n    count\n    isDefault\n    description\n    creator {\n      id\n      name\n    }\n    collaborators {\n      id\n      name\n    }\n    informationTemplate {\n      lot\n      name\n      required\n      description\n      defaultValue\n    }\n  }\n}\n\nquery UserIntegrations {\n  userIntegrations {\n    id\n    lot\n    provider\n    createdOn\n    isDisabled\n    maximumProgress\n    minimumProgress\n    lastTriggeredOn\n    syncToOwnedCollection\n  }\n}\n\nquery UserNotificationPlatforms {\n  userNotificationPlatforms {\n    id\n    lot\n    createdOn\n    isDisabled\n    description\n  }\n}\n\nquery UsersList($query: String) {\n  usersList(query: $query) {\n    id\n    lot\n    name\n    isDisabled\n  }\n}\n\nquery UserMetadataRecommendations {\n  userMetadataRecommendations\n}\n\nquery UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {\n  userUpcomingCalendarEvents(input: $input) {\n    ...CalendarEventPart\n  }\n}\n\nquery UserCalendarEvents($input: UserCalendarEventInput!) {\n  userCalendarEvents(input: $input) {\n    date\n    events {\n      ...CalendarEventPart\n    }\n  }\n}\n\nquery MetadataPartialDetails($metadataId: String!) {\n  metadataPartialDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    image\n    publishYear\n  }\n}\n\nquery MetadataGroupsList($input: MetadataGroupsListInput!) {\n  metadataGroupsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery PeopleList($input: PeopleListInput!) {\n  peopleList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAccessLinks {\n  userAccessLinks {\n    id\n    name\n    isDemo\n    createdOn\n    expiresOn\n    timesUsed\n    isRevoked\n    maximumUses\n    isAccountDefault\n    isMutationAllowed\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    name\n    level\n    force\n    source\n    muscles\n    mechanic\n    equipment\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery GenreDetails($input: GenreDetailsInput!) {\n  genreDetails(input: $input) {\n    details {\n      id\n      name\n      numItems\n    }\n    contents {\n      details {\n        total\n        nextPage\n      }\n      items\n    }\n  }\n}\n\nquery CollectionContents($input: CollectionContentsInput!) {\n  collectionContents(input: $input) {\n    user {\n      id\n      name\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    results {\n      details {\n        total\n        nextPage\n      }\n      items {\n        entityId\n        entityLot\n      }\n    }\n    details {\n      name\n      description\n      createdOn\n    }\n  }\n}\n\nquery CoreDetails {\n  coreDetails {\n    version\n    docsLink\n    pageSize\n    websiteUrl\n    smtpEnabled\n    oidcEnabled\n    signupAllowed\n    repositoryLink\n    disableTelemetry\n    tokenValidForDays\n    localAuthDisabled\n    fileStorageEnabled\n    isServerKeyValidated\n    metadataLotSourceMappings {\n      lot\n      sources\n    }\n    metadataProviderLanguages {\n      source\n      default\n      supported\n    }\n    frontend {\n      url\n      oidcButtonLabel\n      dashboardMessage\n      umami {\n        domains\n        scriptUrl\n        websiteId\n      }\n    }\n    exerciseParameters {\n      downloadRequired\n      filters {\n        type\n        level\n        force\n        mechanic\n        equipment\n        muscle\n      }\n      lotMapping {\n        lot\n        bests\n      }\n    }\n  }\n}\n\nquery MetadataGroupDetails($metadataGroupId: String!) {\n  metadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    contents\n    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}"): (typeof documents)["query GetOidcRedirectUrl {\n  getOidcRedirectUrl\n}\n\nquery UserByOidcIssuerId($oidcIssuerId: String!) {\n  userByOidcIssuerId(oidcIssuerId: $oidcIssuerId)\n}\n\nquery GetOidcToken($code: String!) {\n  getOidcToken(code: $code) {\n    subject\n    email\n  }\n}\n\nquery GetPresignedS3Url($key: String!) {\n  getPresignedS3Url(key: $key)\n}\n\nquery UserExports {\n  userExports {\n    url\n    key\n    size\n    endedAt\n    startedAt\n  }\n}\n\nquery UserCollectionsList($name: String) {\n  userCollectionsList(name: $name) {\n    id\n    name\n    count\n    isDefault\n    description\n    creator {\n      id\n      name\n    }\n    collaborators {\n      id\n      name\n    }\n    informationTemplate {\n      lot\n      name\n      required\n      description\n      defaultValue\n    }\n  }\n}\n\nquery UserIntegrations {\n  userIntegrations {\n    id\n    lot\n    provider\n    createdOn\n    isDisabled\n    maximumProgress\n    minimumProgress\n    lastTriggeredOn\n    syncToOwnedCollection\n  }\n}\n\nquery UserNotificationPlatforms {\n  userNotificationPlatforms {\n    id\n    lot\n    createdOn\n    isDisabled\n    description\n  }\n}\n\nquery UsersList($query: String) {\n  usersList(query: $query) {\n    id\n    lot\n    name\n    isDisabled\n  }\n}\n\nquery UserMetadataRecommendations {\n  userMetadataRecommendations\n}\n\nquery UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {\n  userUpcomingCalendarEvents(input: $input) {\n    ...CalendarEventPart\n  }\n}\n\nquery UserCalendarEvents($input: UserCalendarEventInput!) {\n  userCalendarEvents(input: $input) {\n    date\n    events {\n      ...CalendarEventPart\n    }\n  }\n}\n\nquery MetadataPartialDetails($metadataId: String!) {\n  metadataPartialDetails(metadataId: $metadataId) {\n    id\n    lot\n    title\n    image\n    publishYear\n  }\n}\n\nquery MetadataGroupsList($input: MetadataGroupsListInput!) {\n  metadataGroupsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery PeopleList($input: PeopleListInput!) {\n  peopleList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAccessLinks {\n  userAccessLinks {\n    id\n    name\n    isDemo\n    createdOn\n    expiresOn\n    timesUsed\n    isRevoked\n    maximumUses\n    isAccountDefault\n    isMutationAllowed\n  }\n}\n\nquery ExerciseDetails($exerciseId: String!) {\n  exerciseDetails(exerciseId: $exerciseId) {\n    id\n    lot\n    name\n    level\n    force\n    source\n    muscles\n    mechanic\n    equipment\n    createdByUserId\n    attributes {\n      instructions\n      images\n    }\n  }\n}\n\nquery ExercisesList($input: ExercisesListInput!) {\n  exercisesList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery ImportReports {\n  importReports {\n    id\n    source\n    progress\n    startedOn\n    finishedOn\n    wasSuccess\n    estimatedFinishTime\n    details {\n      import {\n        total\n      }\n      failedItems {\n        lot\n        step\n        error\n        identifier\n      }\n    }\n  }\n}\n\nquery GenresList($input: SearchInput!) {\n  genresList(input: $input) {\n    items\n    details {\n      total\n      nextPage\n    }\n  }\n}\n\nquery GenreDetails($input: GenreDetailsInput!) {\n  genreDetails(input: $input) {\n    details {\n      id\n      name\n      numItems\n    }\n    contents {\n      details {\n        total\n        nextPage\n      }\n      items\n    }\n  }\n}\n\nquery CollectionContents($input: CollectionContentsInput!) {\n  collectionContents(input: $input) {\n    user {\n      id\n      name\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n    results {\n      details {\n        total\n        nextPage\n      }\n      items {\n        entityId\n        entityLot\n      }\n    }\n    details {\n      name\n      description\n      createdOn\n    }\n  }\n}\n\nquery CoreDetails {\n  coreDetails {\n    version\n    docsLink\n    pageSize\n    websiteUrl\n    smtpEnabled\n    oidcEnabled\n    signupAllowed\n    repositoryLink\n    disableTelemetry\n    tokenValidForDays\n    localAuthDisabled\n    fileStorageEnabled\n    isServerKeyValidated\n    metadataLotSourceMappings {\n      lot\n      sources\n    }\n    metadataProviderLanguages {\n      source\n      default\n      supported\n    }\n    frontend {\n      url\n      oidcButtonLabel\n      dashboardMessage\n      umami {\n        domains\n        scriptUrl\n        websiteId\n      }\n    }\n    exerciseParameters {\n      downloadRequired\n      filters {\n        type\n        level\n        force\n        mechanic\n        equipment\n        muscle\n      }\n      lotMapping {\n        lot\n        bests\n      }\n    }\n  }\n}\n\nquery MetadataGroupDetails($metadataGroupId: String!) {\n  metadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    contents\n    details {\n      id\n      lot\n      title\n      parts\n      source\n      isPartial\n      sourceUrl\n      identifier\n      description\n      displayImages\n    }\n  }\n}\n\nquery MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n  metadataGroupSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      parts\n    }\n  }\n}\n\nquery MetadataList($input: MetadataListInput!) {\n  metadataList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery MetadataSearch($input: MetadataSearchInput!) {\n  metadataSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      title\n      image\n      publishYear\n    }\n  }\n}\n\nquery PeopleSearch($input: PeopleSearchInput!) {\n  peopleSearch(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items {\n      identifier\n      name\n      image\n      birthYear\n    }\n  }\n}\n\nquery UserMetadataGroupDetails($metadataGroupId: String!) {\n  userMetadataGroupDetails(metadataGroupId: $metadataGroupId) {\n    recentlyConsumed\n    reviews {\n      ...ReviewItemPart\n    }\n    collections {\n      ...CollectionPart\n    }\n  }\n}\n\nquery UserPersonDetails($personId: String!) {\n  userPersonDetails(personId: $personId) {\n    recentlyConsumed\n    collections {\n      ...CollectionPart\n    }\n    reviews {\n      ...ReviewItemPart\n    }\n  }\n}\n\nquery UserWorkoutDetails($workoutId: String!) {\n  userWorkoutDetails(workoutId: $workoutId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      endTime\n      duration\n      startTime\n      templateId\n      repeatedFrom\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutsList($input: SearchInput!) {\n  userWorkoutsList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserWorkoutTemplateDetails($workoutTemplateId: String!) {\n  userWorkoutTemplateDetails(workoutTemplateId: $workoutTemplateId) {\n    collections {\n      ...CollectionPart\n    }\n    details {\n      id\n      name\n      createdOn\n      summary {\n        ...WorkoutSummaryPart\n      }\n      information {\n        ...WorkoutInformationPart\n      }\n    }\n  }\n}\n\nquery UserWorkoutTemplatesList($input: SearchInput!) {\n  userWorkoutTemplatesList(input: $input) {\n    details {\n      total\n      nextPage\n    }\n    items\n  }\n}\n\nquery UserAnalyticsParameters {\n  userAnalyticsParameters {\n    endDate\n    startDate\n  }\n}"];
 /**
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
  */
diff --git a/libs/generated/src/graphql/backend/graphql.ts b/libs/generated/src/graphql/backend/graphql.ts
index 2fa3ba4b41..adf7a38722 100644
--- a/libs/generated/src/graphql/backend/graphql.ts
+++ b/libs/generated/src/graphql/backend/graphql.ts
@@ -363,6 +363,7 @@ export type DailyUserActivityItem = {
   day: Scalars['NaiveDate']['output'];
   mangaCount: Scalars['Int']['output'];
   movieCount: Scalars['Int']['output'];
+  musicCount: Scalars['Int']['output'];
   podcastCount: Scalars['Int']['output'];
   showCount: Scalars['Int']['output'];
   totalAudioBookDuration: Scalars['Int']['output'];
@@ -374,6 +375,7 @@ export type DailyUserActivityItem = {
   totalMetadataGroupReviewCount: Scalars['Int']['output'];
   totalMetadataReviewCount: Scalars['Int']['output'];
   totalMovieDuration: Scalars['Int']['output'];
+  totalMusicDuration: Scalars['Int']['output'];
   totalPersonReviewCount: Scalars['Int']['output'];
   totalPodcastDuration: Scalars['Int']['output'];
   totalReviewCount: Scalars['Int']['output'];
@@ -644,6 +646,7 @@ export type ExercisesListInput = {
 
 export type ExportJob = {
   endedAt: Scalars['DateTime']['output'];
+  key: Scalars['String']['output'];
   size: Scalars['Int']['output'];
   startedAt: Scalars['DateTime']['output'];
   url: Scalars['String']['output'];
@@ -702,11 +705,6 @@ export type GenreListItem = {
   numItems?: Maybe<Scalars['Int']['output']>;
 };
 
-export type GenreListResults = {
-  details: SearchDetails;
-  items: Array<GenreListItem>;
-};
-
 export type GraphqlCalendarEvent = {
   animeExtraInformation?: Maybe<SeenAnimeExtraInformation>;
   calendarEventId: Scalars['String']['output'];
@@ -899,7 +897,8 @@ export enum IntegrationProvider {
   PlexSink = 'PLEX_SINK',
   PlexYank = 'PLEX_YANK',
   Radarr = 'RADARR',
-  Sonarr = 'SONARR'
+  Sonarr = 'SONARR',
+  YoutubeMusic = 'YOUTUBE_MUSIC'
 }
 
 export type IntegrationSourceSpecificsInput = {
@@ -925,6 +924,7 @@ export type IntegrationSourceSpecificsInput = {
   sonarrProfileId?: InputMaybe<Scalars['Int']['input']>;
   sonarrRootFolderPath?: InputMaybe<Scalars['String']['input']>;
   sonarrSyncCollectionIds?: InputMaybe<Array<Scalars['String']['input']>>;
+  youtubeMusicAuthCookie?: InputMaybe<Scalars['String']['input']>;
 };
 
 export type LoginError = {
@@ -1240,8 +1240,8 @@ export type MutationRoot = {
   presignedPutS3Url: PresignedPutUrlResponse;
   /** Get an access token using an access link. */
   processAccessLink: ProcessAccessLinkResult;
-  /** Refresh the user recommendations key. */
-  refreshUserRecommendationsKey: Scalars['Boolean']['output'];
+  /** Refresh the user metadata recommendations. */
+  refreshUserMetadataRecommendations: Scalars['Boolean']['output'];
   /**
    * Create a new user for the service. Also set their `lot` as admin if
    * they are the first user.
@@ -1738,7 +1738,7 @@ export type QueryRoot = {
   /** Get details about a genre present in the database. */
   genreDetails: GenreDetails;
   /** Get paginated list of genres. */
-  genresList: GenreListResults;
+  genresList: IdResults;
   /** Get an authorization URL using the configured OIDC client. */
   getOidcRedirectUrl: Scalars['String']['output'];
   /** Get an access token using the configured OIDC client. */
@@ -1793,12 +1793,12 @@ export type QueryRoot = {
   userMetadataDetails: UserMetadataDetails;
   /** Get details that can be displayed to a user for a metadata group. */
   userMetadataGroupDetails: UserMetadataGroupDetails;
+  /** Get metadata recommendations for the currently logged in user. */
+  userMetadataRecommendations: Array<Scalars['String']['output']>;
   /** Get all the notification platforms for the currently logged in user. */
   userNotificationPlatforms: Array<NotificationPlatform>;
   /** Get details that can be displayed to a user for a creator. */
   userPersonDetails: UserPersonDetails;
-  /** Get media recommendations for the currently logged in user. */
-  userRecommendations: Array<Scalars['String']['output']>;
   /** Get upcoming calendar events for the given filter. */
   userUpcomingCalendarEvents: Array<GraphqlCalendarEvent>;
   /** Get details about a workout. */
@@ -2583,37 +2583,19 @@ export type UserMeasurementsListInput = {
 };
 
 export type UserMediaFeaturesEnabledPreferences = {
-  anime: Scalars['Boolean']['output'];
-  audioBook: Scalars['Boolean']['output'];
-  book: Scalars['Boolean']['output'];
   enabled: Scalars['Boolean']['output'];
   genres: Scalars['Boolean']['output'];
   groups: Scalars['Boolean']['output'];
-  manga: Scalars['Boolean']['output'];
-  movie: Scalars['Boolean']['output'];
-  music: Scalars['Boolean']['output'];
   people: Scalars['Boolean']['output'];
-  podcast: Scalars['Boolean']['output'];
-  show: Scalars['Boolean']['output'];
-  videoGame: Scalars['Boolean']['output'];
-  visualNovel: Scalars['Boolean']['output'];
+  specific: Array<MediaLot>;
 };
 
 export type UserMediaFeaturesEnabledPreferencesInput = {
-  anime: Scalars['Boolean']['input'];
-  audioBook: Scalars['Boolean']['input'];
-  book: Scalars['Boolean']['input'];
   enabled: Scalars['Boolean']['input'];
   genres: Scalars['Boolean']['input'];
   groups: Scalars['Boolean']['input'];
-  manga: Scalars['Boolean']['input'];
-  movie: Scalars['Boolean']['input'];
-  music: Scalars['Boolean']['input'];
   people: Scalars['Boolean']['input'];
-  podcast: Scalars['Boolean']['input'];
-  show: Scalars['Boolean']['input'];
-  videoGame: Scalars['Boolean']['input'];
-  visualNovel: Scalars['Boolean']['input'];
+  specific: Array<MediaLot>;
 };
 
 export type UserMediaNextEntry = {
@@ -3370,10 +3352,10 @@ export type MergeExerciseMutationVariables = Exact<{
 
 export type MergeExerciseMutation = { mergeExercise: boolean };
 
-export type RefreshUserRecommendationsKeyMutationVariables = Exact<{ [key: string]: never; }>;
+export type RefreshUserMetadataRecommendationsMutationVariables = Exact<{ [key: string]: never; }>;
 
 
-export type RefreshUserRecommendationsKeyMutation = { refreshUserRecommendationsKey: boolean };
+export type RefreshUserMetadataRecommendationsMutation = { refreshUserMetadataRecommendations: boolean };
 
 export type MarkEntityAsPartialMutationVariables = Exact<{
   input: MarkEntityAsPartialInput;
@@ -3401,12 +3383,12 @@ export type UserAnalyticsQueryVariables = Exact<{
 }>;
 
 
-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 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, musicCount: 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, totalMusicDuration: 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, promptForRestTimer: 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<UserNotificationContent>, enabled: boolean }, featuresEnabled: { analytics: { enabled: boolean }, others: { calendar: boolean, collections: boolean }, fitness: { enabled: boolean, workouts: boolean, templates: boolean, measurements: boolean }, media: { enabled: boolean, show: boolean, book: boolean, anime: boolean, manga: boolean, music: boolean, movie: boolean, groups: boolean, people: boolean, genres: boolean, podcast: boolean, audioBook: boolean, videoGame: boolean, visualNovel: 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, promptForRestTimer: 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<UserNotificationContent>, enabled: boolean }, featuresEnabled: { analytics: { enabled: boolean }, others: { calendar: boolean, collections: boolean }, fitness: { enabled: boolean, workouts: boolean, templates: boolean, measurements: boolean }, media: { enabled: boolean, groups: boolean, people: boolean, genres: boolean, specific: Array<MediaLot> } } } } | { __typename: 'UserDetailsError' } };
 
 export type UserExerciseDetailsQueryVariables = Exact<{
   exerciseId: Scalars['String']['input'];
@@ -3458,7 +3440,7 @@ export type GetPresignedS3UrlQuery = { getPresignedS3Url: string };
 export type UserExportsQueryVariables = Exact<{ [key: string]: never; }>;
 
 
-export type UserExportsQuery = { userExports: Array<{ url: string, size: number, endedAt: string, startedAt: string }> };
+export type UserExportsQuery = { userExports: Array<{ url: string, key: string, size: number, endedAt: string, startedAt: string }> };
 
 export type UserCollectionsListQueryVariables = Exact<{
   name?: InputMaybe<Scalars['String']['input']>;
@@ -3484,10 +3466,10 @@ export type UsersListQueryVariables = Exact<{
 
 export type UsersListQuery = { usersList: Array<{ id: string, lot: UserLot, name: string, isDisabled?: boolean | null }> };
 
-export type UserRecommendationsQueryVariables = Exact<{ [key: string]: never; }>;
+export type UserMetadataRecommendationsQueryVariables = Exact<{ [key: string]: never; }>;
 
 
-export type UserRecommendationsQuery = { userRecommendations: Array<string> };
+export type UserMetadataRecommendationsQuery = { userMetadataRecommendations: Array<string> };
 
 export type UserUpcomingCalendarEventsQueryVariables = Exact<{
   input: UserUpcomingCalendarEventInput;
@@ -3553,7 +3535,7 @@ export type GenresListQueryVariables = Exact<{
 }>;
 
 
-export type GenresListQuery = { genresList: { details: { total: number, nextPage?: number | null }, items: Array<{ id: string, name: string, numItems?: number | null }> } };
+export type GenresListQuery = { genresList: { items: Array<string>, details: { total: number, nextPage?: number | null } } };
 
 export type GenreDetailsQueryVariables = Exact<{
   input: GenreDetailsInput;
@@ -3759,12 +3741,12 @@ 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 RefreshUserRecommendationsKeyDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RefreshUserRecommendationsKey"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"refreshUserRecommendationsKey"}}]}}]} as unknown as DocumentNode<RefreshUserRecommendationsKeyMutation, RefreshUserRecommendationsKeyMutationVariables>;
+export const RefreshUserMetadataRecommendationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RefreshUserMetadataRecommendations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"refreshUserMetadataRecommendations"}}]}}]} as unknown as DocumentNode<RefreshUserMetadataRecommendationsMutation, RefreshUserMetadataRecommendationsMutationVariables>;
 export const MarkEntityAsPartialDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MarkEntityAsPartial"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MarkEntityAsPartialInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"markEntityAsPartial"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<MarkEntityAsPartialMutation, MarkEntityAsPartialMutationVariables>;
 export const MetadataDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MetadataDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"metadataDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"metadataId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"isNsfw"}},{"kind":"Field","name":{"kind":"Name","value":"isPartial"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"suggestions"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}},{"kind":"Field","name":{"kind":"Name","value":"publishDate"}},{"kind":"Field","name":{"kind":"Name","value":"providerRating"}},{"kind":"Field","name":{"kind":"Name","value":"createdByUserId"}},{"kind":"Field","name":{"kind":"Name","value":"productionStatus"}},{"kind":"Field","name":{"kind":"Name","value":"originalLanguage"}},{"kind":"Field","name":{"kind":"Name","value":"genres"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"part"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"images"}},{"kind":"Field","name":{"kind":"Name","value":"videos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"videoId"}},{"kind":"Field","name":{"kind":"Name","value":"source"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"creators"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"character"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"watchProviders"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"languages"}}]}},{"kind":"Field","name":{"kind":"Name","value":"animeSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodes"}}]}},{"kind":"Field","name":{"kind":"Name","value":"audioBookSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}}]}},{"kind":"Field","name":{"kind":"Name","value":"bookSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pages"}}]}},{"kind":"Field","name":{"kind":"Name","value":"movieSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}}]}},{"kind":"Field","name":{"kind":"Name","value":"mangaSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"volumes"}},{"kind":"Field","name":{"kind":"Name","value":"chapters"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"overview"}},{"kind":"Field","name":{"kind":"Name","value":"thumbnail"}},{"kind":"Field","name":{"kind":"Name","value":"number"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"publishDate"}}]}},{"kind":"Field","name":{"kind":"Name","value":"totalEpisodes"}}]}},{"kind":"Field","name":{"kind":"Name","value":"showSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalSeasons"}},{"kind":"Field","name":{"kind":"Name","value":"totalEpisodes"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"seasons"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"seasonNumber"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"overview"}},{"kind":"Field","name":{"kind":"Name","value":"backdropImages"}},{"kind":"Field","name":{"kind":"Name","value":"posterImages"}},{"kind":"Field","name":{"kind":"Name","value":"episodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"posterImages"}},{"kind":"Field","name":{"kind":"Name","value":"episodeNumber"}},{"kind":"Field","name":{"kind":"Name","value":"publishDate"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"overview"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"visualNovelSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"length"}}]}},{"kind":"Field","name":{"kind":"Name","value":"videoGameSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"platforms"}}]}},{"kind":"Field","name":{"kind":"Name","value":"musicSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"viewCount"}},{"kind":"Field","name":{"kind":"Name","value":"byVariousArtists"}}]}}]}}]}}]} as unknown as DocumentNode<MetadataDetailsQuery, MetadataDetailsQueryVariables>;
 export const PersonDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PersonDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"personId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"personId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"personId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"associatedMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PersonDetailsGroupedByRolePart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"associatedMetadataGroups"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PersonDetailsGroupedByRolePart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"place"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"gender"}},{"kind":"Field","name":{"kind":"Name","value":"website"}},{"kind":"Field","name":{"kind":"Name","value":"deathDate"}},{"kind":"Field","name":{"kind":"Name","value":"birthDate"}},{"kind":"Field","name":{"kind":"Name","value":"isPartial"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"displayImages"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PersonDetailsGroupedByRolePart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PersonDetailsGroupedByRole"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"entityId"}},{"kind":"Field","name":{"kind":"Name","value":"character"}}]}}]}}]} as unknown as DocumentNode<PersonDetailsQuery, PersonDetailsQueryVariables>;
-export const 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":"promptForRestTimer"}},{"kind":"Field","name":{"kind":"Name","value":"showDetailsWhileEditing"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unitSystem"}},{"kind":"Field","name":{"kind":"Name","value":"setRestTimers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SetRestTimersPart"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"measurements"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"custom"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"dataType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"inbuilt"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"bodyMassIndex"}},{"kind":"Field","name":{"kind":"Name","value":"totalBodyWater"}},{"kind":"Field","name":{"kind":"Name","value":"muscle"}},{"kind":"Field","name":{"kind":"Name","value":"leanBodyMass"}},{"kind":"Field","name":{"kind":"Name","value":"bodyFat"}},{"kind":"Field","name":{"kind":"Name","value":"boneMass"}},{"kind":"Field","name":{"kind":"Name","value":"visceralFat"}},{"kind":"Field","name":{"kind":"Name","value":"waistCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"waistToHeightRatio"}},{"kind":"Field","name":{"kind":"Name","value":"hipCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"waistToHipRatio"}},{"kind":"Field","name":{"kind":"Name","value":"chestCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"thighCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"bicepsCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"neckCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"bodyFatCaliper"}},{"kind":"Field","name":{"kind":"Name","value":"chestSkinfold"}},{"kind":"Field","name":{"kind":"Name","value":"abdominalSkinfold"}},{"kind":"Field","name":{"kind":"Name","value":"thighSkinfold"}},{"kind":"Field","name":{"kind":"Name","value":"basalMetabolicRate"}},{"kind":"Field","name":{"kind":"Name","value":"totalDailyEnergyExpenditure"}},{"kind":"Field","name":{"kind":"Name","value":"calories"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"notifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"toSend"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"featuresEnabled"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"analytics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"others"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"calendar"}},{"kind":"Field","name":{"kind":"Name","value":"collections"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"workouts"}},{"kind":"Field","name":{"kind":"Name","value":"templates"}},{"kind":"Field","name":{"kind":"Name","value":"measurements"}}]}},{"kind":"Field","name":{"kind":"Name","value":"media"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"show"}},{"kind":"Field","name":{"kind":"Name","value":"book"}},{"kind":"Field","name":{"kind":"Name","value":"anime"}},{"kind":"Field","name":{"kind":"Name","value":"manga"}},{"kind":"Field","name":{"kind":"Name","value":"music"}},{"kind":"Field","name":{"kind":"Name","value":"movie"}},{"kind":"Field","name":{"kind":"Name","value":"groups"}},{"kind":"Field","name":{"kind":"Name","value":"people"}},{"kind":"Field","name":{"kind":"Name","value":"genres"}},{"kind":"Field","name":{"kind":"Name","value":"podcast"}},{"kind":"Field","name":{"kind":"Name","value":"audioBook"}},{"kind":"Field","name":{"kind":"Name","value":"videoGame"}},{"kind":"Field","name":{"kind":"Name","value":"visualNovel"}}]}}]}}]}}]}}]}}]}},{"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 UserAnalyticsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserAnalytics"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UserAnalyticsInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userAnalytics"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hours"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hour"}},{"kind":"Field","name":{"kind":"Name","value":"entities"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"entityId"}},{"kind":"Field","name":{"kind":"Name","value":"entityLot"}},{"kind":"Field","name":{"kind":"Name","value":"metadataLot"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"activities"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"groupedBy"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalDuration"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"day"}},{"kind":"Field","name":{"kind":"Name","value":"bookCount"}},{"kind":"Field","name":{"kind":"Name","value":"showCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"movieCount"}},{"kind":"Field","name":{"kind":"Name","value":"musicCount"}},{"kind":"Field","name":{"kind":"Name","value":"animeCount"}},{"kind":"Field","name":{"kind":"Name","value":"mangaCount"}},{"kind":"Field","name":{"kind":"Name","value":"workoutCount"}},{"kind":"Field","name":{"kind":"Name","value":"podcastCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalBookPages"}},{"kind":"Field","name":{"kind":"Name","value":"audioBookCount"}},{"kind":"Field","name":{"kind":"Name","value":"videoGameCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"visualNovelCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutReps"}},{"kind":"Field","name":{"kind":"Name","value":"totalShowDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalMovieDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalMusicDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalMetadataCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutWeight"}},{"kind":"Field","name":{"kind":"Name","value":"totalPodcastDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutDistance"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutRestTime"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutDuration"}},{"kind":"Field","name":{"kind":"Name","value":"userMeasurementCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalVideoGameDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalAudioBookDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalPersonReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalVisualNovelDuration"}},{"kind":"Field","name":{"kind":"Name","value":"totalMetadataReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalWorkoutPersonalBests"}},{"kind":"Field","name":{"kind":"Name","value":"totalCollectionReviewCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalMetadataGroupReviewCount"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workoutReps"}},{"kind":"Field","name":{"kind":"Name","value":"workoutCount"}},{"kind":"Field","name":{"kind":"Name","value":"workoutWeight"}},{"kind":"Field","name":{"kind":"Name","value":"workoutDistance"}},{"kind":"Field","name":{"kind":"Name","value":"workoutDuration"}},{"kind":"Field","name":{"kind":"Name","value":"workoutRestTime"}},{"kind":"Field","name":{"kind":"Name","value":"measurementCount"}},{"kind":"Field","name":{"kind":"Name","value":"workoutPersonalBests"}},{"kind":"Field","name":{"kind":"Name","value":"workoutExercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"exercise"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workoutMuscles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"muscle"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workoutEquipments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"equipment"}}]}}]}}]}}]}}]} as unknown as DocumentNode<UserAnalyticsQuery, UserAnalyticsQueryVariables>;
+export const UserDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserDetails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userDetails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"isDisabled"}},{"kind":"Field","name":{"kind":"Name","value":"oidcIssuerId"}},{"kind":"Field","name":{"kind":"Name","value":"preferences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"general"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reviewScale"}},{"kind":"Field","name":{"kind":"Name","value":"gridPacking"}},{"kind":"Field","name":{"kind":"Name","value":"displayNsfw"}},{"kind":"Field","name":{"kind":"Name","value":"disableVideos"}},{"kind":"Field","name":{"kind":"Name","value":"persistQueries"}},{"kind":"Field","name":{"kind":"Name","value":"disableReviews"}},{"kind":"Field","name":{"kind":"Name","value":"disableIntegrations"}},{"kind":"Field","name":{"kind":"Name","value":"disableWatchProviders"}},{"kind":"Field","name":{"kind":"Name","value":"disableNavigationAnimation"}},{"kind":"Field","name":{"kind":"Name","value":"dashboard"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hidden"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"numElements"}},{"kind":"Field","name":{"kind":"Name","value":"deduplicateMedia"}}]}},{"kind":"Field","name":{"kind":"Name","value":"watchProviders"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"values"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logging"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"muteSounds"}},{"kind":"Field","name":{"kind":"Name","value":"promptForRestTimer"}},{"kind":"Field","name":{"kind":"Name","value":"showDetailsWhileEditing"}}]}},{"kind":"Field","name":{"kind":"Name","value":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unitSystem"}},{"kind":"Field","name":{"kind":"Name","value":"setRestTimers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SetRestTimersPart"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"measurements"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"custom"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"dataType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"inbuilt"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"bodyMassIndex"}},{"kind":"Field","name":{"kind":"Name","value":"totalBodyWater"}},{"kind":"Field","name":{"kind":"Name","value":"muscle"}},{"kind":"Field","name":{"kind":"Name","value":"leanBodyMass"}},{"kind":"Field","name":{"kind":"Name","value":"bodyFat"}},{"kind":"Field","name":{"kind":"Name","value":"boneMass"}},{"kind":"Field","name":{"kind":"Name","value":"visceralFat"}},{"kind":"Field","name":{"kind":"Name","value":"waistCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"waistToHeightRatio"}},{"kind":"Field","name":{"kind":"Name","value":"hipCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"waistToHipRatio"}},{"kind":"Field","name":{"kind":"Name","value":"chestCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"thighCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"bicepsCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"neckCircumference"}},{"kind":"Field","name":{"kind":"Name","value":"bodyFatCaliper"}},{"kind":"Field","name":{"kind":"Name","value":"chestSkinfold"}},{"kind":"Field","name":{"kind":"Name","value":"abdominalSkinfold"}},{"kind":"Field","name":{"kind":"Name","value":"thighSkinfold"}},{"kind":"Field","name":{"kind":"Name","value":"basalMetabolicRate"}},{"kind":"Field","name":{"kind":"Name","value":"totalDailyEnergyExpenditure"}},{"kind":"Field","name":{"kind":"Name","value":"calories"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"notifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"toSend"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"featuresEnabled"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"analytics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"others"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"calendar"}},{"kind":"Field","name":{"kind":"Name","value":"collections"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"workouts"}},{"kind":"Field","name":{"kind":"Name","value":"templates"}},{"kind":"Field","name":{"kind":"Name","value":"measurements"}}]}},{"kind":"Field","name":{"kind":"Name","value":"media"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"groups"}},{"kind":"Field","name":{"kind":"Name","value":"people"}},{"kind":"Field","name":{"kind":"Name","value":"genres"}},{"kind":"Field","name":{"kind":"Name","value":"specific"}}]}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SetRestTimersPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SetRestTimersSettings"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"drop"}},{"kind":"Field","name":{"kind":"Name","value":"warmup"}},{"kind":"Field","name":{"kind":"Name","value":"normal"}},{"kind":"Field","name":{"kind":"Name","value":"failure"}}]}}]} as unknown as DocumentNode<UserDetailsQuery, UserDetailsQueryVariables>;
 export const UserExerciseDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserExerciseDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"exerciseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userExerciseDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"exerciseId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"exerciseId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"collections"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CollectionPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"reviews"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ReviewItemPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"history"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"idx"}},{"kind":"Field","name":{"kind":"Name","value":"workoutId"}},{"kind":"Field","name":{"kind":"Name","value":"workoutEndOn"}},{"kind":"Field","name":{"kind":"Name","value":"bestSet"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetRecordPart"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"exerciseId"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedOn"}},{"kind":"Field","name":{"kind":"Name","value":"exerciseNumTimesInteracted"}},{"kind":"Field","name":{"kind":"Name","value":"exerciseExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"settings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"excludeFromAnalytics"}},{"kind":"Field","name":{"kind":"Name","value":"setRestTimers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SetRestTimersPart"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"lifetimeStats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"personalBestsAchieved"}}]}},{"kind":"Field","name":{"kind":"Name","value":"personalBests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"sets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"setIdx"}},{"kind":"Field","name":{"kind":"Name","value":"workoutId"}},{"kind":"Field","name":{"kind":"Name","value":"exerciseIdx"}}]}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenAnimeExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenMangaExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenMangaExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"volume"}},{"kind":"Field","name":{"kind":"Name","value":"chapter"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutSetStatisticPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSetStatistic"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reps"}},{"kind":"Field","name":{"kind":"Name","value":"pace"}},{"kind":"Field","name":{"kind":"Name","value":"oneRm"}},{"kind":"Field","name":{"kind":"Name","value":"weight"}},{"kind":"Field","name":{"kind":"Name","value":"volume"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"distance"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CollectionPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Collection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ReviewItemPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ReviewItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"rating"}},{"kind":"Field","name":{"kind":"Name","value":"postedOn"}},{"kind":"Field","name":{"kind":"Name","value":"isSpoiler"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"textOriginal"}},{"kind":"Field","name":{"kind":"Name","value":"textRendered"}},{"kind":"Field","name":{"kind":"Name","value":"seenItemsAssociatedWith"}},{"kind":"Field","name":{"kind":"Name","value":"postedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"likedBy"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"showExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"Field","name":{"kind":"Name","value":"animeExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"mangaExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenMangaExtraInformationPart"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkoutSetRecordPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkoutSetRecord"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"personalBests"}},{"kind":"Field","name":{"kind":"Name","value":"statistic"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkoutSetStatisticPart"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SetRestTimersPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SetRestTimersSettings"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"drop"}},{"kind":"Field","name":{"kind":"Name","value":"warmup"}},{"kind":"Field","name":{"kind":"Name","value":"normal"}},{"kind":"Field","name":{"kind":"Name","value":"failure"}}]}}]} as unknown as DocumentNode<UserExerciseDetailsQuery, UserExerciseDetailsQueryVariables>;
 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":"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>;
@@ -3772,12 +3754,12 @@ export const GetOidcRedirectUrlDocument = {"kind":"Document","definitions":[{"ki
 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>;
 export const GetPresignedS3UrlDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetPresignedS3Url"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"key"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getPresignedS3Url"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"key"},"value":{"kind":"Variable","name":{"kind":"Name","value":"key"}}}]}]}}]} as unknown as DocumentNode<GetPresignedS3UrlQuery, GetPresignedS3UrlQueryVariables>;
-export const UserExportsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserExports"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userExports"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"size"}},{"kind":"Field","name":{"kind":"Name","value":"endedAt"}},{"kind":"Field","name":{"kind":"Name","value":"startedAt"}}]}}]}}]} as unknown as DocumentNode<UserExportsQuery, UserExportsQueryVariables>;
+export const UserExportsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserExports"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userExports"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"size"}},{"kind":"Field","name":{"kind":"Name","value":"endedAt"}},{"kind":"Field","name":{"kind":"Name","value":"startedAt"}}]}}]}}]} as unknown as DocumentNode<UserExportsQuery, UserExportsQueryVariables>;
 export const UserCollectionsListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserCollectionsList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userCollectionsList"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"isDefault"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"creator"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"collaborators"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"informationTemplate"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"required"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"defaultValue"}}]}}]}}]}}]} as unknown as DocumentNode<UserCollectionsListQuery, UserCollectionsListQueryVariables>;
 export const UserIntegrationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserIntegrations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userIntegrations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"provider"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}},{"kind":"Field","name":{"kind":"Name","value":"isDisabled"}},{"kind":"Field","name":{"kind":"Name","value":"maximumProgress"}},{"kind":"Field","name":{"kind":"Name","value":"minimumProgress"}},{"kind":"Field","name":{"kind":"Name","value":"lastTriggeredOn"}},{"kind":"Field","name":{"kind":"Name","value":"syncToOwnedCollection"}}]}}]}}]} as unknown as DocumentNode<UserIntegrationsQuery, UserIntegrationsQueryVariables>;
 export const UserNotificationPlatformsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserNotificationPlatforms"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userNotificationPlatforms"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}},{"kind":"Field","name":{"kind":"Name","value":"isDisabled"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]}}]} as unknown as DocumentNode<UserNotificationPlatformsQuery, UserNotificationPlatformsQueryVariables>;
 export const UsersListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UsersList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"query"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"usersList"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"query"},"value":{"kind":"Variable","name":{"kind":"Name","value":"query"}}}],"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"}}]}}]}}]} as unknown as DocumentNode<UsersListQuery, UsersListQueryVariables>;
-export const UserRecommendationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserRecommendations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userRecommendations"}}]}}]} as unknown as DocumentNode<UserRecommendationsQuery, UserRecommendationsQueryVariables>;
+export const UserMetadataRecommendationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserMetadataRecommendations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userMetadataRecommendations"}}]}}]} as unknown as DocumentNode<UserMetadataRecommendationsQuery, UserMetadataRecommendationsQueryVariables>;
 export const UserUpcomingCalendarEventsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserUpcomingCalendarEvents"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UserUpcomingCalendarEventInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userUpcomingCalendarEvents"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CalendarEventPart"}}]}}]}},{"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":"CalendarEventPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GraphqlCalendarEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"metadataId"}},{"kind":"Field","name":{"kind":"Name","value":"metadataLot"}},{"kind":"Field","name":{"kind":"Name","value":"episodeName"}},{"kind":"Field","name":{"kind":"Name","value":"metadataTitle"}},{"kind":"Field","name":{"kind":"Name","value":"metadataImage"}},{"kind":"Field","name":{"kind":"Name","value":"calendarEventId"}},{"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"}}]}}]}}]} as unknown as DocumentNode<UserUpcomingCalendarEventsQuery, UserUpcomingCalendarEventsQueryVariables>;
 export const UserCalendarEventsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserCalendarEvents"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UserCalendarEventInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userCalendarEvents"},"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":"date"}},{"kind":"Field","name":{"kind":"Name","value":"events"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CalendarEventPart"}}]}}]}}]}},{"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":"CalendarEventPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GraphqlCalendarEvent"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"date"}},{"kind":"Field","name":{"kind":"Name","value":"metadataId"}},{"kind":"Field","name":{"kind":"Name","value":"metadataLot"}},{"kind":"Field","name":{"kind":"Name","value":"episodeName"}},{"kind":"Field","name":{"kind":"Name","value":"metadataTitle"}},{"kind":"Field","name":{"kind":"Name","value":"metadataImage"}},{"kind":"Field","name":{"kind":"Name","value":"calendarEventId"}},{"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"}}]}}]}}]} as unknown as DocumentNode<UserCalendarEventsQuery, UserCalendarEventsQueryVariables>;
 export const MetadataPartialDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MetadataPartialDetails"},"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":"metadataPartialDetails"},"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":"image"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}}]}}]}}]} as unknown as DocumentNode<MetadataPartialDetailsQuery, MetadataPartialDetailsQueryVariables>;
@@ -3787,7 +3769,7 @@ export const UserAccessLinksDocument = {"kind":"Document","definitions":[{"kind"
 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":"items"}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}}]}}]}}]}}]} as unknown as DocumentNode<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":"estimatedFinishTime"}},{"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 GenresListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GenresList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SearchInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"genresList"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}}]}}]}}]}}]} as unknown as DocumentNode<GenresListQuery, GenresListQueryVariables>;
 export const GenreDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GenreDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GenreDetailsInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"genreDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"numItems"}}]}},{"kind":"Field","name":{"kind":"Name","value":"contents"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}}]}},{"kind":"Field","name":{"kind":"Name","value":"items"}}]}}]}}]}}]} as unknown as DocumentNode<GenreDetailsQuery, GenreDetailsQueryVariables>;
 export const CollectionContentsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CollectionContents"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CollectionContentsInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"collectionContents"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"reviews"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ReviewItemPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"results"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}}]}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"entityId"}},{"kind":"Field","name":{"kind":"Name","value":"entityLot"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenAnimeExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SeenMangaExtraInformationPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"SeenMangaExtraInformation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"volume"}},{"kind":"Field","name":{"kind":"Name","value":"chapter"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ReviewItemPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ReviewItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"rating"}},{"kind":"Field","name":{"kind":"Name","value":"postedOn"}},{"kind":"Field","name":{"kind":"Name","value":"isSpoiler"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"textOriginal"}},{"kind":"Field","name":{"kind":"Name","value":"textRendered"}},{"kind":"Field","name":{"kind":"Name","value":"seenItemsAssociatedWith"}},{"kind":"Field","name":{"kind":"Name","value":"postedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"comments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"likedBy"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"showExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}},{"kind":"Field","name":{"kind":"Name","value":"animeExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenAnimeExtraInformationPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"mangaExtraInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenMangaExtraInformationPart"}}]}}]}}]} as unknown as DocumentNode<CollectionContentsQuery, CollectionContentsQueryVariables>;
 export const CoreDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CoreDetails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"coreDetails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"docsLink"}},{"kind":"Field","name":{"kind":"Name","value":"pageSize"}},{"kind":"Field","name":{"kind":"Name","value":"websiteUrl"}},{"kind":"Field","name":{"kind":"Name","value":"smtpEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"oidcEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"signupAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"repositoryLink"}},{"kind":"Field","name":{"kind":"Name","value":"disableTelemetry"}},{"kind":"Field","name":{"kind":"Name","value":"tokenValidForDays"}},{"kind":"Field","name":{"kind":"Name","value":"localAuthDisabled"}},{"kind":"Field","name":{"kind":"Name","value":"fileStorageEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"isServerKeyValidated"}},{"kind":"Field","name":{"kind":"Name","value":"metadataLotSourceMappings"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"sources"}}]}},{"kind":"Field","name":{"kind":"Name","value":"metadataProviderLanguages"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"default"}},{"kind":"Field","name":{"kind":"Name","value":"supported"}}]}},{"kind":"Field","name":{"kind":"Name","value":"frontend"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"oidcButtonLabel"}},{"kind":"Field","name":{"kind":"Name","value":"dashboardMessage"}},{"kind":"Field","name":{"kind":"Name","value":"umami"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domains"}},{"kind":"Field","name":{"kind":"Name","value":"scriptUrl"}},{"kind":"Field","name":{"kind":"Name","value":"websiteId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"exerciseParameters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"downloadRequired"}},{"kind":"Field","name":{"kind":"Name","value":"filters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"level"}},{"kind":"Field","name":{"kind":"Name","value":"force"}},{"kind":"Field","name":{"kind":"Name","value":"mechanic"}},{"kind":"Field","name":{"kind":"Name","value":"equipment"}},{"kind":"Field","name":{"kind":"Name","value":"muscle"}}]}},{"kind":"Field","name":{"kind":"Name","value":"lotMapping"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"bests"}}]}}]}}]}}]}}]} as unknown as DocumentNode<CoreDetailsQuery, CoreDetailsQueryVariables>;
diff --git a/libs/generated/src/graphql/backend/types.generated.ts b/libs/generated/src/graphql/backend/types.generated.ts
index b1b3f4824e..f41e17b5c8 100644
--- a/libs/generated/src/graphql/backend/types.generated.ts
+++ b/libs/generated/src/graphql/backend/types.generated.ts
@@ -353,6 +353,7 @@ export type DailyUserActivityItem = {
   day: Scalars['NaiveDate']['output'];
   mangaCount: Scalars['Int']['output'];
   movieCount: Scalars['Int']['output'];
+  musicCount: Scalars['Int']['output'];
   podcastCount: Scalars['Int']['output'];
   showCount: Scalars['Int']['output'];
   totalAudioBookDuration: Scalars['Int']['output'];
@@ -364,6 +365,7 @@ export type DailyUserActivityItem = {
   totalMetadataGroupReviewCount: Scalars['Int']['output'];
   totalMetadataReviewCount: Scalars['Int']['output'];
   totalMovieDuration: Scalars['Int']['output'];
+  totalMusicDuration: Scalars['Int']['output'];
   totalPersonReviewCount: Scalars['Int']['output'];
   totalPodcastDuration: Scalars['Int']['output'];
   totalReviewCount: Scalars['Int']['output'];
@@ -643,6 +645,7 @@ export type ExercisesListInput = {
 export type ExportJob = {
   __typename?: 'ExportJob';
   endedAt: Scalars['DateTime']['output'];
+  key: Scalars['String']['output'];
   size: Scalars['Int']['output'];
   startedAt: Scalars['DateTime']['output'];
   url: Scalars['String']['output'];
@@ -708,12 +711,6 @@ export type GenreListItem = {
   numItems?: Maybe<Scalars['Int']['output']>;
 };
 
-export type GenreListResults = {
-  __typename?: 'GenreListResults';
-  details: SearchDetails;
-  items: Array<GenreListItem>;
-};
-
 export type GraphqlCalendarEvent = {
   __typename?: 'GraphqlCalendarEvent';
   animeExtraInformation?: Maybe<SeenAnimeExtraInformation>;
@@ -921,7 +918,8 @@ export enum IntegrationProvider {
   PlexSink = 'PLEX_SINK',
   PlexYank = 'PLEX_YANK',
   Radarr = 'RADARR',
-  Sonarr = 'SONARR'
+  Sonarr = 'SONARR',
+  YoutubeMusic = 'YOUTUBE_MUSIC'
 }
 
 export type IntegrationSourceSpecificsInput = {
@@ -947,6 +945,7 @@ export type IntegrationSourceSpecificsInput = {
   sonarrProfileId?: InputMaybe<Scalars['Int']['input']>;
   sonarrRootFolderPath?: InputMaybe<Scalars['String']['input']>;
   sonarrSyncCollectionIds?: InputMaybe<Array<Scalars['String']['input']>>;
+  youtubeMusicAuthCookie?: InputMaybe<Scalars['String']['input']>;
 };
 
 export type LoginError = {
@@ -1279,8 +1278,8 @@ export type MutationRoot = {
   presignedPutS3Url: PresignedPutUrlResponse;
   /** Get an access token using an access link. */
   processAccessLink: ProcessAccessLinkResult;
-  /** Refresh the user recommendations key. */
-  refreshUserRecommendationsKey: Scalars['Boolean']['output'];
+  /** Refresh the user metadata recommendations. */
+  refreshUserMetadataRecommendations: Scalars['Boolean']['output'];
   /**
    * Create a new user for the service. Also set their `lot` as admin if
    * they are the first user.
@@ -1792,7 +1791,7 @@ export type QueryRoot = {
   /** Get details about a genre present in the database. */
   genreDetails: GenreDetails;
   /** Get paginated list of genres. */
-  genresList: GenreListResults;
+  genresList: IdResults;
   /** Get an authorization URL using the configured OIDC client. */
   getOidcRedirectUrl: Scalars['String']['output'];
   /** Get an access token using the configured OIDC client. */
@@ -1847,12 +1846,12 @@ export type QueryRoot = {
   userMetadataDetails: UserMetadataDetails;
   /** Get details that can be displayed to a user for a metadata group. */
   userMetadataGroupDetails: UserMetadataGroupDetails;
+  /** Get metadata recommendations for the currently logged in user. */
+  userMetadataRecommendations: Array<Scalars['String']['output']>;
   /** Get all the notification platforms for the currently logged in user. */
   userNotificationPlatforms: Array<NotificationPlatform>;
   /** Get details that can be displayed to a user for a creator. */
   userPersonDetails: UserPersonDetails;
-  /** Get media recommendations for the currently logged in user. */
-  userRecommendations: Array<Scalars['String']['output']>;
   /** Get upcoming calendar events for the given filter. */
   userUpcomingCalendarEvents: Array<GraphqlCalendarEvent>;
   /** Get details about a workout. */
@@ -2672,37 +2671,19 @@ export type UserMeasurementsListInput = {
 
 export type UserMediaFeaturesEnabledPreferences = {
   __typename?: 'UserMediaFeaturesEnabledPreferences';
-  anime: Scalars['Boolean']['output'];
-  audioBook: Scalars['Boolean']['output'];
-  book: Scalars['Boolean']['output'];
   enabled: Scalars['Boolean']['output'];
   genres: Scalars['Boolean']['output'];
   groups: Scalars['Boolean']['output'];
-  manga: Scalars['Boolean']['output'];
-  movie: Scalars['Boolean']['output'];
-  music: Scalars['Boolean']['output'];
   people: Scalars['Boolean']['output'];
-  podcast: Scalars['Boolean']['output'];
-  show: Scalars['Boolean']['output'];
-  videoGame: Scalars['Boolean']['output'];
-  visualNovel: Scalars['Boolean']['output'];
+  specific: Array<MediaLot>;
 };
 
 export type UserMediaFeaturesEnabledPreferencesInput = {
-  anime: Scalars['Boolean']['input'];
-  audioBook: Scalars['Boolean']['input'];
-  book: Scalars['Boolean']['input'];
   enabled: Scalars['Boolean']['input'];
   genres: Scalars['Boolean']['input'];
   groups: Scalars['Boolean']['input'];
-  manga: Scalars['Boolean']['input'];
-  movie: Scalars['Boolean']['input'];
-  music: Scalars['Boolean']['input'];
   people: Scalars['Boolean']['input'];
-  podcast: Scalars['Boolean']['input'];
-  show: Scalars['Boolean']['input'];
-  videoGame: Scalars['Boolean']['input'];
-  visualNovel: Scalars['Boolean']['input'];
+  specific: Array<MediaLot>;
 };
 
 export type UserMediaNextEntry = {
diff --git a/libs/graphql/src/backend/mutations/combined.gql b/libs/graphql/src/backend/mutations/combined.gql
index b2910ee0ca..d5a7762f37 100644
--- a/libs/graphql/src/backend/mutations/combined.gql
+++ b/libs/graphql/src/backend/mutations/combined.gql
@@ -263,8 +263,8 @@ mutation MergeExercise($mergeFrom: String!, $mergeInto: String!) {
 	mergeExercise(mergeFrom: $mergeFrom, mergeInto: $mergeInto)
 }
 
-mutation RefreshUserRecommendationsKey {
-	refreshUserRecommendationsKey
+mutation RefreshUserMetadataRecommendations {
+	refreshUserMetadataRecommendations
 }
 
 mutation MarkEntityAsPartial($input: MarkEntityAsPartialInput!) {
diff --git a/libs/graphql/src/backend/queries/UserAnalytics.gql b/libs/graphql/src/backend/queries/UserAnalytics.gql
index dc1ffd2275..01ef9c5b30 100644
--- a/libs/graphql/src/backend/queries/UserAnalytics.gql
+++ b/libs/graphql/src/backend/queries/UserAnalytics.gql
@@ -18,6 +18,7 @@ query UserAnalytics($input: UserAnalyticsInput!) {
 				showCount
 				totalCount
 				movieCount
+				musicCount
 				animeCount
 				mangaCount
 				workoutCount
@@ -31,6 +32,7 @@ query UserAnalytics($input: UserAnalyticsInput!) {
 				totalWorkoutReps
 				totalShowDuration
 				totalMovieDuration
+				totalMusicDuration
 				totalMetadataCount
 				totalWorkoutWeight
 				totalPodcastDuration
diff --git a/libs/graphql/src/backend/queries/UserDetails.gql b/libs/graphql/src/backend/queries/UserDetails.gql
index 7bd27b3b60..5d4eebbc08 100644
--- a/libs/graphql/src/backend/queries/UserDetails.gql
+++ b/libs/graphql/src/backend/queries/UserDetails.gql
@@ -93,19 +93,10 @@ query UserDetails {
 					}
 					media {
 						enabled
-						show
-						book
-						anime
-						manga
-						music
-						movie
 						groups
 						people
 						genres
-						podcast
-						audioBook
-						videoGame
-						visualNovel
+						specific
 					}
 				}
 			}
diff --git a/libs/graphql/src/backend/queries/combined.gql b/libs/graphql/src/backend/queries/combined.gql
index 90513594d4..cbca79ac1c 100644
--- a/libs/graphql/src/backend/queries/combined.gql
+++ b/libs/graphql/src/backend/queries/combined.gql
@@ -20,6 +20,7 @@ query GetPresignedS3Url($key: String!) {
 query UserExports {
 	userExports {
 		url
+		key
 		size
 		endedAt
 		startedAt
@@ -84,8 +85,8 @@ query UsersList($query: String) {
 	}
 }
 
-query UserRecommendations {
-	userRecommendations
+query UserMetadataRecommendations {
+	userMetadataRecommendations
 }
 
 query UserUpcomingCalendarEvents($input: UserUpcomingCalendarEventInput!) {
@@ -202,15 +203,11 @@ query ImportReports {
 
 query GenresList($input: SearchInput!) {
 	genresList(input: $input) {
+		items
 		details {
 			total
 			nextPage
 		}
-		items {
-			id
-			name
-			numItems
-		}
 	}
 }
 


Test patch: