From b7a9510d08e87d11b654bbb2082571d7f18ff036 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 13:20:20 +0530 Subject: [PATCH 01/29] fix(backend): user agent now honors MusicBrainz format --- apps/backend/src/utils.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/backend/src/utils.rs b/apps/backend/src/utils.rs index 62112c133f..a506472548 100644 --- a/apps/backend/src/utils.rs +++ b/apps/backend/src/utils.rs @@ -40,13 +40,23 @@ use crate::{ pub type MemoryDatabase = Arc>; -pub static VERSION: &str = env!("CARGO_PKG_VERSION"); pub static BASE_DIR: &str = env!("CARGO_MANIFEST_DIR"); +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const PROJECT_NAME: &str = env!("CARGO_PKG_NAME"); pub const PAGE_LIMIT: i32 = 20; pub const COOKIE_NAME: &str = "auth"; pub const AUTHOR: &str = "ignisda"; -pub const USER_AGENT_STR: &str = const_str::concat!(AUTHOR, "/", PROJECT_NAME); +pub const AUTHOR_EMAIL: &str = "ignisda2001@gmail.com"; +pub const USER_AGENT_STR: &str = const_str::concat!( + AUTHOR, + "/", + PROJECT_NAME, + "-v", + VERSION, + " (", + AUTHOR_EMAIL, + ")" +); pub const AVATAR_URL: &str = "https://raw.githubusercontent.com/IgnisDa/ryot/main/apps/frontend/public/icon-512x512.png"; From db9d6e2abff68d8db2694514865bbaded29cd3a8 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 13:28:58 +0530 Subject: [PATCH 02/29] feat(backend): add enum variants for music --- apps/backend/src/migrator/m20230410_create_metadata.rs | 4 ++++ apps/backend/src/miscellaneous/resolver.rs | 5 +++++ libs/generated/src/graphql/backend/graphql.ts | 2 ++ 3 files changed, 11 insertions(+) diff --git a/apps/backend/src/migrator/m20230410_create_metadata.rs b/apps/backend/src/migrator/m20230410_create_metadata.rs index abd15b9f01..6589452daf 100644 --- a/apps/backend/src/migrator/m20230410_create_metadata.rs +++ b/apps/backend/src/migrator/m20230410_create_metadata.rs @@ -69,6 +69,8 @@ pub enum MetadataSource { Itunes, #[sea_orm(string_value = "LI")] Listennotes, + #[sea_orm(string_value = "MU")] + MusicBrainz, #[sea_orm(string_value = "OL")] Openlibrary, #[sea_orm(string_value = "TM")] @@ -106,6 +108,8 @@ pub enum MetadataLot { Manga, #[sea_orm(string_value = "MO")] Movie, + #[sea_orm(string_value = "MU")] + Music, #[sea_orm(string_value = "SH")] Show, #[sea_orm(string_value = "VG")] diff --git a/apps/backend/src/miscellaneous/resolver.rs b/apps/backend/src/miscellaneous/resolver.rs index 4afbce8b37..68b5f181d3 100644 --- a/apps/backend/src/miscellaneous/resolver.rs +++ b/apps/backend/src/miscellaneous/resolver.rs @@ -1334,6 +1334,7 @@ impl MiscellaneousService { let identifier = &model.identifier; let source_url = match model.source { MetadataSource::Custom => None, + MetadataSource::MusicBrainz => todo!(), MetadataSource::Itunes => Some(format!( "https://podcasts.apple.com/us/podcast/{slug}/id{identifier}" )), @@ -2616,6 +2617,7 @@ impl MiscellaneousService { fn get_provider(&self, lot: MetadataLot, source: MetadataSource) -> Result { let err = || Err(Error::new("This source is not supported".to_owned())); let service: Provider = match source { + MetadataSource::MusicBrainz => todo!(), MetadataSource::Openlibrary => Box::new(self.openlibrary_service.clone()), MetadataSource::Itunes => Box::new(self.itunes_service.clone()), MetadataSource::GoogleBooks => Box::new(self.google_books_service.clone()), @@ -3428,6 +3430,7 @@ impl MiscellaneousService { })) }; let specifics = match input.lot { + MetadataLot::Music => todo!(), MetadataLot::AudioBook => match input.audio_book_specifics { None => return err(), Some(ref mut s) => MediaSpecifics::AudioBook(s.clone()), @@ -4100,6 +4103,7 @@ impl MiscellaneousService { async fn media_sources_for_lot(&self, lot: MetadataLot) -> Vec { match lot { + MetadataLot::Music => vec![MetadataSource::MusicBrainz], MetadataLot::AudioBook => vec![MetadataSource::Audible], MetadataLot::Book => vec![MetadataSource::Openlibrary, MetadataSource::GoogleBooks], MetadataLot::Podcast => vec![MetadataSource::Itunes, MetadataSource::Listennotes], @@ -4113,6 +4117,7 @@ impl MiscellaneousService { MetadataSource::iter() .map(|source| { let (supported, default) = match source { + MetadataSource::MusicBrainz => todo!(), MetadataSource::Itunes => ( ITunesService::supported_languages(), ITunesService::default_language(), diff --git a/libs/generated/src/graphql/backend/graphql.ts b/libs/generated/src/graphql/backend/graphql.ts index 343fbc8dac..092899bfb4 100644 --- a/libs/generated/src/graphql/backend/graphql.ts +++ b/libs/generated/src/graphql/backend/graphql.ts @@ -592,6 +592,7 @@ export enum MetadataLot { Book = 'BOOK', Manga = 'MANGA', Movie = 'MOVIE', + Music = 'MUSIC', Podcast = 'PODCAST', Show = 'SHOW', VideoGame = 'VIDEO_GAME' @@ -605,6 +606,7 @@ export enum MetadataSource { Igdb = 'IGDB', Itunes = 'ITUNES', Listennotes = 'LISTENNOTES', + MusicBrainz = 'MUSIC_BRAINZ', Openlibrary = 'OPENLIBRARY', Tmdb = 'TMDB' } From 486b2351f2872f1d75c41de619d138a02f18d646 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 13:31:08 +0530 Subject: [PATCH 03/29] fix(frontend): handle music --- apps/frontend/src/lib/utilities.ts | 3 +++ apps/frontend/src/pages/media/item/index.tsx | 1 + 2 files changed, 4 insertions(+) diff --git a/apps/frontend/src/lib/utilities.ts b/apps/frontend/src/lib/utilities.ts index 8089a5a38f..66c8dd2717 100644 --- a/apps/frontend/src/lib/utilities.ts +++ b/apps/frontend/src/lib/utilities.ts @@ -11,6 +11,7 @@ import { IconDeviceTvOld, IconHeadphones, IconMicrophone, + IconMusic, } from "@tabler/icons-react"; import { match } from "ts-pattern"; @@ -25,6 +26,7 @@ export const getLot = (lot: unknown) => { .with("manga", "mangas", () => MetadataLot.Manga) .with("books", "book", () => MetadataLot.Book) .with("movies", "movie", () => MetadataLot.Movie) + .with("music", () => MetadataLot.Music) .with("tv", "show", "shows", () => MetadataLot.Show) .with( "games", @@ -137,5 +139,6 @@ export const getMetadataIcon = (lot: MetadataLot) => { .with(MetadataLot.Podcast, () => IconMicrophone) .with(MetadataLot.Manga, () => IconDeviceTvOld) .with(MetadataLot.Anime, () => IconBooks) + .with(MetadataLot.Music, () => IconMusic) .exhaustive(); }; diff --git a/apps/frontend/src/pages/media/item/index.tsx b/apps/frontend/src/pages/media/item/index.tsx index 43c1314213..ed47685c55 100644 --- a/apps/frontend/src/pages/media/item/index.tsx +++ b/apps/frontend/src/pages/media/item/index.tsx @@ -575,6 +575,7 @@ const Page: NextPageWithLayout = () => { from: "yellow", to: "purple", })) + .with(MetadataLot.Music, () => ({ from: "orange", to: "yellow" })) .exhaustive(); const source = mediaDetails?.data?.source || MetadataSource.Custom; From 8743bd9391ba8c466b583447d2ddf9e705f46a56 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 13:33:14 +0530 Subject: [PATCH 04/29] feat(backend): allow enabling or disabling music --- apps/backend/src/miscellaneous/resolver.rs | 3 +++ apps/backend/src/users.rs | 3 +++ libs/generated/src/graphql/backend/gql.ts | 4 ++-- libs/generated/src/graphql/backend/graphql.ts | 5 +++-- libs/graphql/src/backend/queries/UserPreferences.gql | 1 + 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/backend/src/miscellaneous/resolver.rs b/apps/backend/src/miscellaneous/resolver.rs index 68b5f181d3..94a8bea269 100644 --- a/apps/backend/src/miscellaneous/resolver.rs +++ b/apps/backend/src/miscellaneous/resolver.rs @@ -3781,6 +3781,9 @@ impl MiscellaneousService { "movie" => { preferences.features_enabled.media.movie = value_bool.unwrap() } + "music" => { + preferences.features_enabled.media.music = value_bool.unwrap() + } "podcast" => { preferences.features_enabled.media.podcast = value_bool.unwrap() } diff --git a/apps/backend/src/users.rs b/apps/backend/src/users.rs index 22e6eb6c4a..adc611f25c 100644 --- a/apps/backend/src/users.rs +++ b/apps/backend/src/users.rs @@ -47,6 +47,8 @@ pub struct UserMediaFeaturesEnabledPreferences { #[serde(default)] pub movie: bool, #[serde(default)] + pub music: bool, + #[serde(default)] pub podcast: bool, #[serde(default)] pub show: bool, @@ -63,6 +65,7 @@ impl Default for UserMediaFeaturesEnabledPreferences { book: true, manga: true, movie: true, + music: true, podcast: true, show: true, video_game: true, diff --git a/libs/generated/src/graphql/backend/gql.ts b/libs/generated/src/graphql/backend/gql.ts index 90da96c424..f686cc774e 100644 --- a/libs/generated/src/graphql/backend/gql.ts +++ b/libs/generated/src/graphql/backend/gql.ts @@ -75,7 +75,7 @@ const documents = { "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, "fragment SeenPart on Seen {\n id\n progress\n state\n startedOn\n finishedOn\n lastUpdatedOn\n showInformation {\n episode\n season\n }\n podcastInformation {\n episode\n }\n}\n\nquery UserMediaDetails($metadataId: Int!) {\n userMediaDetails(metadataId: $metadataId) {\n collections {\n id\n name\n }\n inProgress {\n ...SeenPart\n }\n history {\n ...SeenPart\n }\n reviews {\n id\n rating\n text\n spoiler\n visibility\n showSeason\n showEpisode\n podcastEpisode\n postedOn\n postedBy {\n id\n name\n }\n }\n reminder {\n remindOn\n message\n }\n isMonitored\n seenBy\n nextEpisode {\n seasonNumber\n episodeNumber\n }\n }\n}": types.SeenPartFragmentDoc, "query UserNotificationPlatforms {\n userNotificationPlatforms {\n id\n description\n timestamp\n }\n}": types.UserNotificationPlatformsDocument, - "query UserPreferences {\n userPreferences {\n fitness {\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 exercises {\n saveHistory\n weightUnit\n distanceUnit\n }\n }\n notifications {\n episodeReleased\n statusChanged\n releaseDateChanged\n numberOfSeasonsChanged\n }\n featuresEnabled {\n fitness {\n enabled\n }\n media {\n enabled\n anime\n audioBook\n book\n manga\n movie\n podcast\n show\n videoGame\n }\n }\n }\n}": types.UserPreferencesDocument, + "query UserPreferences {\n userPreferences {\n fitness {\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 exercises {\n saveHistory\n weightUnit\n distanceUnit\n }\n }\n notifications {\n episodeReleased\n statusChanged\n releaseDateChanged\n numberOfSeasonsChanged\n }\n featuresEnabled {\n fitness {\n enabled\n }\n media {\n enabled\n anime\n audioBook\n book\n manga\n movie\n music\n podcast\n show\n videoGame\n }\n }\n }\n}": types.UserPreferencesDocument, "query UsersList {\n usersList {\n id\n name\n lot\n }\n}": types.UsersListDocument, }; @@ -344,7 +344,7 @@ export function graphql(source: "query UserNotificationPlatforms {\n userNotifi /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query UserPreferences {\n userPreferences {\n fitness {\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 exercises {\n saveHistory\n weightUnit\n distanceUnit\n }\n }\n notifications {\n episodeReleased\n statusChanged\n releaseDateChanged\n numberOfSeasonsChanged\n }\n featuresEnabled {\n fitness {\n enabled\n }\n media {\n enabled\n anime\n audioBook\n book\n manga\n movie\n podcast\n show\n videoGame\n }\n }\n }\n}"): (typeof documents)["query UserPreferences {\n userPreferences {\n fitness {\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 exercises {\n saveHistory\n weightUnit\n distanceUnit\n }\n }\n notifications {\n episodeReleased\n statusChanged\n releaseDateChanged\n numberOfSeasonsChanged\n }\n featuresEnabled {\n fitness {\n enabled\n }\n media {\n enabled\n anime\n audioBook\n book\n manga\n movie\n podcast\n show\n videoGame\n }\n }\n }\n}"]; +export function graphql(source: "query UserPreferences {\n userPreferences {\n fitness {\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 exercises {\n saveHistory\n weightUnit\n distanceUnit\n }\n }\n notifications {\n episodeReleased\n statusChanged\n releaseDateChanged\n numberOfSeasonsChanged\n }\n featuresEnabled {\n fitness {\n enabled\n }\n media {\n enabled\n anime\n audioBook\n book\n manga\n movie\n music\n podcast\n show\n videoGame\n }\n }\n }\n}"): (typeof documents)["query UserPreferences {\n userPreferences {\n fitness {\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 exercises {\n saveHistory\n weightUnit\n distanceUnit\n }\n }\n notifications {\n episodeReleased\n statusChanged\n releaseDateChanged\n numberOfSeasonsChanged\n }\n featuresEnabled {\n fitness {\n enabled\n }\n media {\n enabled\n anime\n audioBook\n book\n manga\n movie\n music\n podcast\n show\n videoGame\n }\n }\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 092899bfb4..5b55df0309 100644 --- a/libs/generated/src/graphql/backend/graphql.ts +++ b/libs/generated/src/graphql/backend/graphql.ts @@ -1429,6 +1429,7 @@ export type UserMediaFeaturesEnabledPreferences = { enabled: Scalars['Boolean']['output']; manga: Scalars['Boolean']['output']; movie: Scalars['Boolean']['output']; + music: Scalars['Boolean']['output']; podcast: Scalars['Boolean']['output']; show: Scalars['Boolean']['output']; videoGame: Scalars['Boolean']['output']; @@ -1925,7 +1926,7 @@ export type UserNotificationPlatformsQuery = { userNotificationPlatforms: Array< export type UserPreferencesQueryVariables = Exact<{ [key: string]: never; }>; -export type UserPreferencesQuery = { userPreferences: { fitness: { 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 } }, exercises: { saveHistory: number, weightUnit: UserWeightUnit, distanceUnit: UserDistanceUnit } }, notifications: { episodeReleased: boolean, statusChanged: boolean, releaseDateChanged: boolean, numberOfSeasonsChanged: boolean }, featuresEnabled: { fitness: { enabled: boolean }, media: { enabled: boolean, anime: boolean, audioBook: boolean, book: boolean, manga: boolean, movie: boolean, podcast: boolean, show: boolean, videoGame: boolean } } } }; +export type UserPreferencesQuery = { userPreferences: { fitness: { 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 } }, exercises: { saveHistory: number, weightUnit: UserWeightUnit, distanceUnit: UserDistanceUnit } }, notifications: { episodeReleased: boolean, statusChanged: boolean, releaseDateChanged: boolean, numberOfSeasonsChanged: boolean }, featuresEnabled: { fitness: { enabled: boolean }, media: { enabled: boolean, anime: boolean, audioBook: boolean, book: boolean, manga: boolean, movie: boolean, music: boolean, podcast: boolean, show: boolean, videoGame: boolean } } } }; export type UsersListQueryVariables = Exact<{ [key: string]: never; }>; @@ -1995,5 +1996,5 @@ export const UserIntegrationsDocument = {"kind":"Document","definitions":[{"kind 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; export const UserMediaDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserMediaDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userMediaDetails"},"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":"collections"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"inProgress"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"history"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SeenPart"}}]}},{"kind":"Field","name":{"kind":"Name","value":"reviews"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"rating"}},{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"spoiler"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"showSeason"}},{"kind":"Field","name":{"kind":"Name","value":"showEpisode"}},{"kind":"Field","name":{"kind":"Name","value":"podcastEpisode"}},{"kind":"Field","name":{"kind":"Name","value":"postedOn"}},{"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":"reminder"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"remindOn"}},{"kind":"Field","name":{"kind":"Name","value":"message"}}]}},{"kind":"Field","name":{"kind":"Name","value":"isMonitored"}},{"kind":"Field","name":{"kind":"Name","value":"seenBy"}},{"kind":"Field","name":{"kind":"Name","value":"nextEpisode"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seasonNumber"}},{"kind":"Field","name":{"kind":"Name","value":"episodeNumber"}}]}}]}}]}},{"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":"progress"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"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":"showInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}},{"kind":"Field","name":{"kind":"Name","value":"season"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcastInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episode"}}]}}]}}]} as unknown as DocumentNode; 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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}}]}}]}}]} as unknown as DocumentNode; -export const UserPreferencesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserPreferences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userPreferences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"saveHistory"}},{"kind":"Field","name":{"kind":"Name","value":"weightUnit"}},{"kind":"Field","name":{"kind":"Name","value":"distanceUnit"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"notifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodeReleased"}},{"kind":"Field","name":{"kind":"Name","value":"statusChanged"}},{"kind":"Field","name":{"kind":"Name","value":"releaseDateChanged"}},{"kind":"Field","name":{"kind":"Name","value":"numberOfSeasonsChanged"}}]}},{"kind":"Field","name":{"kind":"Name","value":"featuresEnabled"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"media"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"anime"}},{"kind":"Field","name":{"kind":"Name","value":"audioBook"}},{"kind":"Field","name":{"kind":"Name","value":"book"}},{"kind":"Field","name":{"kind":"Name","value":"manga"}},{"kind":"Field","name":{"kind":"Name","value":"movie"}},{"kind":"Field","name":{"kind":"Name","value":"podcast"}},{"kind":"Field","name":{"kind":"Name","value":"show"}},{"kind":"Field","name":{"kind":"Name","value":"videoGame"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const UserPreferencesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserPreferences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userPreferences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"exercises"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"saveHistory"}},{"kind":"Field","name":{"kind":"Name","value":"weightUnit"}},{"kind":"Field","name":{"kind":"Name","value":"distanceUnit"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"notifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodeReleased"}},{"kind":"Field","name":{"kind":"Name","value":"statusChanged"}},{"kind":"Field","name":{"kind":"Name","value":"releaseDateChanged"}},{"kind":"Field","name":{"kind":"Name","value":"numberOfSeasonsChanged"}}]}},{"kind":"Field","name":{"kind":"Name","value":"featuresEnabled"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}},{"kind":"Field","name":{"kind":"Name","value":"media"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"anime"}},{"kind":"Field","name":{"kind":"Name","value":"audioBook"}},{"kind":"Field","name":{"kind":"Name","value":"book"}},{"kind":"Field","name":{"kind":"Name","value":"manga"}},{"kind":"Field","name":{"kind":"Name","value":"movie"}},{"kind":"Field","name":{"kind":"Name","value":"music"}},{"kind":"Field","name":{"kind":"Name","value":"podcast"}},{"kind":"Field","name":{"kind":"Name","value":"show"}},{"kind":"Field","name":{"kind":"Name","value":"videoGame"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const UsersListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UsersList"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"usersList"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/libs/graphql/src/backend/queries/UserPreferences.gql b/libs/graphql/src/backend/queries/UserPreferences.gql index e6b0e92fb6..4ad7cc027a 100644 --- a/libs/graphql/src/backend/queries/UserPreferences.gql +++ b/libs/graphql/src/backend/queries/UserPreferences.gql @@ -55,6 +55,7 @@ query UserPreferences { book manga movie + music podcast show videoGame From 6a5cece6f3efad835128402a5375d5081eefb4d1 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 14:24:39 +0530 Subject: [PATCH 05/29] feat(backend): configuration for music brainz --- apps/backend/src/config.rs | 17 ++ apps/backend/src/miscellaneous/resolver.rs | 11 +- apps/backend/src/providers/mod.rs | 1 + apps/backend/src/providers/music_brainz.rs | 235 +++++++++++++++++++++ docs/includes/backend-config-schema.ts | 12 ++ 5 files changed, 274 insertions(+), 2 deletions(-) create mode 100644 apps/backend/src/providers/music_brainz.rs diff --git a/apps/backend/src/config.rs b/apps/backend/src/config.rs index 8f14219238..cb1e7857fa 100644 --- a/apps/backend/src/config.rs +++ b/apps/backend/src/config.rs @@ -149,6 +149,20 @@ pub struct MovieConfig { pub tmdb: MoviesTmdbConfig, } +#[derive(Debug, Serialize, Deserialize, Clone, Config)] +#[config(rename_all = "snake_case", env_prefix = "MUSIC_BRAINZ_")] +pub struct MusicBrainzConfig { + /// Used for changing the user agent if your requests are being rate limited. + pub user_agent: Option, +} + +#[derive(Debug, Serialize, Deserialize, Clone, Config)] +pub struct MusicConfig { + /// Settings related to Music Brainz. + #[setting(nested)] + pub music_brainz: MusicBrainzConfig, +} + impl IsFeatureEnabled for MovieConfig {} #[derive(Debug, Serialize, Deserialize, Clone, Config)] @@ -440,6 +454,9 @@ pub struct AppConfig { /// Settings related to movies. #[setting(nested)] pub movies: MovieConfig, + /// Settings related to music. + #[setting(nested)] + pub music: MusicConfig, /// Settings related to podcasts. #[setting(nested)] pub podcasts: PodcastConfig, diff --git a/apps/backend/src/miscellaneous/resolver.rs b/apps/backend/src/miscellaneous/resolver.rs index 94a8bea269..8099acd238 100644 --- a/apps/backend/src/miscellaneous/resolver.rs +++ b/apps/backend/src/miscellaneous/resolver.rs @@ -80,6 +80,7 @@ use crate::{ igdb::IgdbService, itunes::ITunesService, listennotes::ListennotesService, + music_brainz::MusicBrainzService, openlibrary::OpenlibraryService, tmdb::{TmdbMovieService, TmdbService, TmdbShowService}, }, @@ -1117,6 +1118,7 @@ pub struct MiscellaneousService { pub tmdb_shows_service: TmdbShowService, pub anilist_anime_service: AnilistAnimeService, pub anilist_manga_service: AnilistMangaService, + pub music_brainz_service: MusicBrainzService, pub integration_service: IntegrationService, pub update_metadata: SqliteStorage, pub recalculate_user_summary: SqliteStorage, @@ -1145,6 +1147,7 @@ impl MiscellaneousService { let google_books_service = GoogleBooksService::new(&config.books.google_books).await; let tmdb_movies_service = TmdbMovieService::new(&config.movies.tmdb).await; let tmdb_shows_service = TmdbShowService::new(&config.shows.tmdb).await; + let music_brainz_service = MusicBrainzService::new(&config.music.music_brainz).await; let audible_service = AudibleService::new(&config.audio_books.audible).await; let igdb_service = IgdbService::new(&config.video_games).await; let itunes_service = ITunesService::new(&config.podcasts.itunes).await; @@ -1178,6 +1181,7 @@ impl MiscellaneousService { tmdb_shows_service, anilist_anime_service, anilist_manga_service, + music_brainz_service, integration_service, update_metadata: update_metadata.clone(), recalculate_user_summary: recalculate_user_summary.clone(), @@ -2617,7 +2621,7 @@ impl MiscellaneousService { fn get_provider(&self, lot: MetadataLot, source: MetadataSource) -> Result { let err = || Err(Error::new("This source is not supported".to_owned())); let service: Provider = match source { - MetadataSource::MusicBrainz => todo!(), + MetadataSource::MusicBrainz => Box::new(self.music_brainz_service.clone()), MetadataSource::Openlibrary => Box::new(self.openlibrary_service.clone()), MetadataSource::Itunes => Box::new(self.itunes_service.clone()), MetadataSource::GoogleBooks => Box::new(self.google_books_service.clone()), @@ -4120,7 +4124,10 @@ impl MiscellaneousService { MetadataSource::iter() .map(|source| { let (supported, default) = match source { - MetadataSource::MusicBrainz => todo!(), + MetadataSource::MusicBrainz => ( + MusicBrainzService::supported_languages(), + MusicBrainzService::default_language(), + ), MetadataSource::Itunes => ( ITunesService::supported_languages(), ITunesService::default_language(), diff --git a/apps/backend/src/providers/mod.rs b/apps/backend/src/providers/mod.rs index a1ba416605..497c3a0fba 100644 --- a/apps/backend/src/providers/mod.rs +++ b/apps/backend/src/providers/mod.rs @@ -4,5 +4,6 @@ pub mod google_books; pub mod igdb; pub mod itunes; pub mod listennotes; +pub mod music_brainz; pub mod openlibrary; pub mod tmdb; diff --git a/apps/backend/src/providers/music_brainz.rs b/apps/backend/src/providers/music_brainz.rs new file mode 100644 index 0000000000..535af0dcf8 --- /dev/null +++ b/apps/backend/src/providers/music_brainz.rs @@ -0,0 +1,235 @@ +use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use convert_case::{Case, Casing}; +use itertools::Itertools; +use serde::{Deserialize, Serialize}; +use surf::{http::headers::USER_AGENT, Client}; + +use crate::{ + config::MusicBrainzConfig, + migrator::{MetadataImageLot, MetadataLot, MetadataSource}, + models::{ + media::{ + BookSpecifics, MediaDetails, MediaSearchItem, MediaSpecifics, MetadataCreator, + MetadataImage, MetadataImageUrl, + }, + SearchResults, + }, + traits::{MediaProvider, MediaProviderLanguages}, + utils::{convert_date_to_year, get_base_http_client, PAGE_LIMIT}, +}; + +pub static URL: &str = "https://www.googleapis.com/books/v1/volumes/"; + +#[derive(Debug, Clone)] +pub struct MusicBrainzService { + client: Client, +} + +impl MediaProviderLanguages for MusicBrainzService { + fn supported_languages() -> Vec { + vec!["us".to_owned()] + } + + fn default_language() -> String { + "us".to_owned() + } +} + +impl MusicBrainzService { + pub async fn new(config: &MusicBrainzConfig) -> Self { + let mut headers = vec![]; + if let Some(ref u) = config.user_agent { + headers.push((USER_AGENT, u.clone())); + } + let client = get_base_http_client(URL, headers); + Self { client } + } +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +struct ImageLinks { + extra_large: Option, + large: Option, + medium: Option, + small: Option, + small_thumbnail: Option, + thumbnail: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +struct ItemVolumeInfo { + title: String, + published_date: Option, + image_links: Option, + description: Option, + authors: Option>, + publisher: Option, + main_category: Option, + categories: Option>, + page_count: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +struct ItemResponse { + id: String, + volume_info: ItemVolumeInfo, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +struct SearchResponse { + total_items: i32, + items: Option>, +} + +#[async_trait] +impl MediaProvider for MusicBrainzService { + async fn details(&self, identifier: &str) -> Result { + let mut rsp = self.client.get(identifier).await.map_err(|e| anyhow!(e))?; + let data: ItemResponse = rsp.body_json().await.map_err(|e| anyhow!(e))?; + let d = self.google_books_response_to_search_response(data.volume_info, data.id); + Ok(d) + } + + async fn search( + &self, + query: &str, + page: Option, + ) -> Result> { + let page = page.unwrap_or(1); + let index = (page - 1) * PAGE_LIMIT; + let mut rsp = self + .client + .get("") + .query(&serde_json::json!({ + "q": format!("intitle:{}", query), + "maxResults": PAGE_LIMIT, + "printType": "books", + "startIndex": index + })) + .unwrap() + .await + .map_err(|e| anyhow!(e))?; + let search: SearchResponse = rsp.body_json().await.map_err(|e| anyhow!(e))?; + let resp = search + .items + .unwrap_or_default() + .into_iter() + .map(|b| { + let MediaDetails { + identifier, + title, + lot, + images, + publish_year, + .. + } = self.google_books_response_to_search_response(b.volume_info, b.id); + let image = images + .into_iter() + .map(|i| match i.url { + MetadataImageUrl::S3(_u) => unreachable!(), + MetadataImageUrl::Url(u) => u, + }) + .collect_vec() + .get(0) + .cloned(); + MediaSearchItem { + identifier, + lot, + title, + image, + publish_year, + } + }) + .collect(); + let next_page = if search.total_items - ((page) * PAGE_LIMIT) > 0 { + Some(page + 1) + } else { + None + }; + Ok(SearchResults { + total: search.total_items, + items: resp, + next_page, + }) + } +} +impl MusicBrainzService { + fn google_books_response_to_search_response( + &self, + item: ItemVolumeInfo, + id: String, + ) -> MediaDetails { + let mut images = vec![]; + if let Some(il) = item.image_links { + if let Some(a) = il.thumbnail { + images.push(a); + } + if let Some(a) = il.small_thumbnail { + images.push(a); + } + if let Some(a) = il.small { + images.push(a); + } + if let Some(a) = il.medium { + images.push(a); + } + if let Some(a) = il.large { + images.push(a); + } + if let Some(a) = il.extra_large { + images.push(a); + } + }; + let images = images.into_iter().map(|a| MetadataImage { + url: MetadataImageUrl::Url(a), + lot: MetadataImageLot::Poster, + }); + let mut creators = item + .authors + .unwrap_or_default() + .into_iter() + .map(|a| MetadataCreator { + name: a, + role: "Author".to_owned(), + image: None, + }) + .collect_vec(); + if let Some(p) = item.publisher { + creators.push(MetadataCreator { + name: p, + role: "Publisher".to_owned(), + image: None, + }); + } + let mut genres = item + .categories + .unwrap_or_default() + .into_iter() + .flat_map(|c| c.split(" / ").map(|g| g.to_case(Case::Title)).collect_vec()) + .collect_vec(); + if let Some(g) = item.main_category { + genres.push(g); + } + MediaDetails { + identifier: id, + lot: MetadataLot::Book, + source: MetadataSource::GoogleBooks, + production_status: "Released".to_owned(), + title: item.title, + description: item.description, + creators: creators.into_iter().unique().collect(), + genres: genres.into_iter().unique().collect(), + publish_year: item.published_date.and_then(|d| convert_date_to_year(&d)), + publish_date: None, + specifics: MediaSpecifics::Book(BookSpecifics { + pages: item.page_count, + }), + images: images.unique().collect(), + } + } +} diff --git a/docs/includes/backend-config-schema.ts b/docs/includes/backend-config-schema.ts index 65e684d9cb..ad39aab57e 100644 --- a/docs/includes/backend-config-schema.ts +++ b/docs/includes/backend-config-schema.ts @@ -132,6 +132,16 @@ export interface MovieConfig { tmdb: MoviesTmdbConfig; } +export interface MusicBrainzConfig { + /** Used for changing the user agent if your requests are being rate limited. */ + user_agent: string | null; +} + +export interface MusicConfig { + /** Settings related to Music Brainz. */ + musicBrainz: MusicBrainzConfig; +} + export interface ITunesConfig { /** The locale to use for making requests to iTunes API. */ locale: string; @@ -294,6 +304,8 @@ export interface AppConfig { media: MediaConfig; /** Settings related to movies. */ movies: MovieConfig; + /** Settings related to music. */ + music: MusicConfig; /** Settings related to podcasts. */ podcasts: PodcastConfig; /** Settings related to scheduler. */ From f93900aeb8d7a0d2e36f6ecd8e32ed6131303459 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 14:41:34 +0530 Subject: [PATCH 06/29] fix(backend): remove serde default --- apps/backend/src/users.rs | 66 +++++++-------------------------------- 1 file changed, 12 insertions(+), 54 deletions(-) diff --git a/apps/backend/src/users.rs b/apps/backend/src/users.rs index adc611f25c..a868b55819 100644 --- a/apps/backend/src/users.rs +++ b/apps/backend/src/users.rs @@ -7,14 +7,11 @@ use strum::EnumString; #[derive( Debug, Serialize, Deserialize, SimpleObject, Clone, Eq, PartialEq, FromJsonQueryResult, )] +#[serde(default)] pub struct UserNotificationsPreferences { - #[serde(default)] pub status_changed: bool, - #[serde(default)] pub episode_released: bool, - #[serde(default)] pub release_date_changed: bool, - #[serde(default)] pub number_of_seasons_changed: bool, } @@ -32,27 +29,18 @@ impl Default for UserNotificationsPreferences { #[derive( Debug, Serialize, Deserialize, SimpleObject, Clone, Eq, PartialEq, FromJsonQueryResult, )] +// FIXME: Remove these +#[serde(default)] pub struct UserMediaFeaturesEnabledPreferences { - // FIXME: Remove these - #[serde(default)] pub enabled: bool, - #[serde(default)] pub anime: bool, - #[serde(default)] pub audio_book: bool, - #[serde(default)] pub book: bool, - #[serde(default)] pub manga: bool, - #[serde(default)] pub movie: bool, - #[serde(default)] pub music: bool, - #[serde(default)] pub podcast: bool, - #[serde(default)] pub show: bool, - #[serde(default)] pub video_game: bool, } @@ -76,8 +64,8 @@ impl Default for UserMediaFeaturesEnabledPreferences { #[derive( Debug, Serialize, Deserialize, SimpleObject, Clone, Eq, PartialEq, FromJsonQueryResult, )] +#[serde(default)] pub struct UserFitnessFeaturesEnabledPreferences { - #[serde(default)] pub enabled: bool, } @@ -130,12 +118,10 @@ pub enum UserDistanceUnit { #[derive( Debug, Serialize, Deserialize, SimpleObject, Clone, Eq, PartialEq, FromJsonQueryResult, )] +#[serde(default)] pub struct UserExercisePreferences { - #[serde(default)] pub save_history: usize, - #[serde(default)] pub distance_unit: UserDistanceUnit, - #[serde(default)] pub weight_unit: UserWeightUnit, } @@ -152,52 +138,30 @@ impl Default for UserExercisePreferences { #[derive( Debug, Serialize, Deserialize, SimpleObject, Clone, Eq, PartialEq, FromJsonQueryResult, )] +#[serde(default)] pub struct UserMeasurementsInBuiltPreferences { - #[serde(default)] pub weight: bool, - #[serde(default)] pub body_mass_index: bool, - #[serde(default)] pub total_body_water: bool, - #[serde(default)] pub muscle: bool, - #[serde(default)] pub body_fat: bool, - #[serde(default)] pub waist_to_height_ratio: bool, - #[serde(default)] pub waist_to_hip_ratio: bool, - #[serde(default)] pub basal_metabolic_rate: bool, - #[serde(default)] pub total_daily_energy_expenditure: bool, - #[serde(default)] pub calories: bool, - #[serde(default)] pub lean_body_mass: bool, - #[serde(default)] pub bone_mass: bool, - #[serde(default)] pub visceral_fat: bool, - #[serde(default)] pub waist_circumference: bool, - #[serde(default)] pub hip_circumference: bool, - #[serde(default)] pub chest_circumference: bool, - #[serde(default)] pub thigh_circumference: bool, - #[serde(default)] pub biceps_circumference: bool, - #[serde(default)] pub neck_circumference: bool, - #[serde(default)] pub body_fat_caliper: bool, - #[serde(default)] pub chest_skinfold: bool, - #[serde(default)] pub abdominal_skinfold: bool, - #[serde(default)] pub thigh_skinfold: bool, } @@ -241,23 +205,21 @@ pub enum UserCustomMeasurementDataType { } #[derive( - Debug, Serialize, Deserialize, SimpleObject, Clone, Eq, PartialEq, FromJsonQueryResult, + Debug, Serialize, Deserialize, SimpleObject, Clone, Eq, PartialEq, FromJsonQueryResult, Default, )] #[serde(rename_all = "camelCase")] +#[serde(default)] pub struct UserCustomMeasurement { - #[serde(default)] pub name: String, - #[serde(default)] pub data_type: UserCustomMeasurementDataType, } #[derive( Debug, Serialize, Deserialize, SimpleObject, Clone, Eq, PartialEq, FromJsonQueryResult, )] +#[serde(default)] pub struct UserMeasurementsPreferences { - #[serde(default)] pub custom: Vec, - #[serde(default)] pub inbuilt: UserMeasurementsInBuiltPreferences, } @@ -276,32 +238,28 @@ impl Default for UserMeasurementsPreferences { #[derive( Debug, Serialize, Deserialize, SimpleObject, Clone, Eq, PartialEq, Default, FromJsonQueryResult, )] +#[serde(default)] pub struct UserFeaturesEnabledPreferences { - #[serde(default)] pub media: UserMediaFeaturesEnabledPreferences, - #[serde(default)] pub fitness: UserFitnessFeaturesEnabledPreferences, } #[derive( Debug, Serialize, Deserialize, SimpleObject, Clone, Eq, PartialEq, Default, FromJsonQueryResult, )] +#[serde(default)] pub struct UserFitnessPreferences { - #[serde(default)] pub exercises: UserExercisePreferences, - #[serde(default)] pub measurements: UserMeasurementsPreferences, } #[derive( Debug, Serialize, Deserialize, SimpleObject, Clone, Eq, PartialEq, Default, FromJsonQueryResult, )] +#[serde(default)] pub struct UserPreferences { - #[serde(default)] pub features_enabled: UserFeaturesEnabledPreferences, - #[serde(default)] pub notifications: UserNotificationsPreferences, - #[serde(default)] pub fitness: UserFitnessPreferences, } From 52ab1dcd4de892fa5d10beebb044858056da0a74 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 15:30:32 +0530 Subject: [PATCH 07/29] feat(backend): get music brainz search working --- apps/backend/src/miscellaneous/resolver.rs | 15 +- apps/backend/src/models.rs | 45 +++-- apps/backend/src/providers/music_brainz.rs | 183 ++++----------------- 3 files changed, 72 insertions(+), 171 deletions(-) diff --git a/apps/backend/src/miscellaneous/resolver.rs b/apps/backend/src/miscellaneous/resolver.rs index 8099acd238..b285e59946 100644 --- a/apps/backend/src/miscellaneous/resolver.rs +++ b/apps/backend/src/miscellaneous/resolver.rs @@ -65,8 +65,8 @@ use crate::{ ImportOrExportItemRating, ImportOrExportItemReview, ImportOrExportItemSeen, MangaSpecifics, MediaCreatorSearchItem, MediaDetails, MediaListItem, MediaSearchItem, MediaSearchItemResponse, MediaSpecifics, MetadataCreator, MetadataImage, - MetadataImageUrl, MetadataImages, MovieSpecifics, PodcastSpecifics, PostReviewInput, - ProgressUpdateError, ProgressUpdateErrorVariant, ProgressUpdateInput, + MetadataImageUrl, MetadataImages, MovieSpecifics, MusicSpecifics, PodcastSpecifics, + PostReviewInput, ProgressUpdateError, ProgressUpdateErrorVariant, ProgressUpdateInput, ProgressUpdateResultUnion, SeenOrReviewExtraInformation, SeenPodcastExtraInformation, SeenShowExtraInformation, ShowSpecifics, UserMediaReminder, UserSummary, VideoGameSpecifics, Visibility, @@ -370,6 +370,7 @@ struct GraphqlMediaDetails { audio_book_specifics: Option, podcast_specifics: Option, manga_specifics: Option, + music_specifics: Option, anime_specifics: Option, source_url: Option, } @@ -1395,6 +1396,7 @@ impl MiscellaneousService { video_game_specifics: None, audio_book_specifics: None, podcast_specifics: None, + music_specifics: None, manga_specifics: None, anime_specifics: None, source_url, @@ -1406,6 +1408,9 @@ impl MiscellaneousService { MediaSpecifics::Book(a) => { resp.book_specifics = Some(a); } + MediaSpecifics::Music(a) => { + resp.music_specifics = Some(a); + } MediaSpecifics::Movie(a) => { resp.movie_specifics = Some(a); } @@ -3224,6 +3229,12 @@ impl MiscellaneousService { ls.media.movies.runtime += r; } } + MediaSpecifics::Music(item) => { + ls.media.music.listened += 1; + if let Some(r) = item.runtime { + ls.media.music.runtime += r; + } + } MediaSpecifics::Show(item) => { unique_shows.insert(seen.metadata_id); for season in item.seasons { diff --git a/apps/backend/src/models.rs b/apps/backend/src/models.rs index 9665d71734..32e660df9b 100644 --- a/apps/backend/src/models.rs +++ b/apps/backend/src/models.rs @@ -119,6 +119,14 @@ pub mod media { pub pages: Option, } + #[derive( + Debug, Serialize, Deserialize, SimpleObject, Clone, InputObject, Eq, PartialEq, Default, + )] + #[graphql(input_name = "MusicSpecificsInput")] + pub struct MusicSpecifics { + pub runtime: Option, + } + #[derive( Debug, Serialize, Deserialize, SimpleObject, Clone, InputObject, Eq, PartialEq, Default, )] @@ -158,8 +166,8 @@ pub mod media { InputObject, )] #[graphql(input_name = "PodcastEpisodeInput")] + #[serde(default)] pub struct PodcastEpisode { - #[serde(default)] pub number: i32, pub id: String, #[serde(rename = "audio_length_sec")] @@ -373,6 +381,22 @@ pub mod media { pub watched: i32, } + #[derive( + SimpleObject, + Debug, + PartialEq, + Eq, + Clone, + Default, + Serialize, + Deserialize, + FromJsonQueryResult, + )] + pub struct MusicSummary { + pub runtime: i32, + pub listened: i32, + } + #[derive( SimpleObject, Debug, @@ -451,26 +475,18 @@ pub mod media { Deserialize, FromJsonQueryResult, )] + #[serde(default)] pub struct UserMediaSummary { - #[serde(default)] pub books: BooksSummary, - #[serde(default)] pub movies: MoviesSummary, - #[serde(default)] + pub music: MusicSummary, pub podcasts: PodcastsSummary, - #[serde(default)] pub shows: ShowsSummary, - #[serde(default)] pub video_games: VideoGamesSummary, - #[serde(default)] pub audio_books: AudioBooksSummary, - #[serde(default)] pub anime: AnimeSummary, - #[serde(default)] pub manga: MangaSummary, - #[serde(default)] pub reviews_posted: u64, - #[serde(default)] pub creators_interacted_with: usize, } @@ -485,8 +501,8 @@ pub mod media { Deserialize, FromJsonQueryResult, )] + #[serde(default)] pub struct UserFitnessSummary { - #[serde(default)] pub measurements_recorded: u64, } @@ -501,12 +517,10 @@ pub mod media { Deserialize, FromJsonQueryResult, )] + #[serde(default)] pub struct UserSummary { - #[serde(default)] pub fitness: UserFitnessSummary, - #[serde(default)] pub media: UserMediaSummary, - #[serde(default)] pub calculated_on: DateTimeUtc, } @@ -649,6 +663,7 @@ pub mod media { AudioBook(AudioBookSpecifics), Book(BookSpecifics), Movie(MovieSpecifics), + Music(MusicSpecifics), Podcast(PodcastSpecifics), Show(ShowSpecifics), VideoGame(VideoGameSpecifics), diff --git a/apps/backend/src/providers/music_brainz.rs b/apps/backend/src/providers/music_brainz.rs index 535af0dcf8..e593701a04 100644 --- a/apps/backend/src/providers/music_brainz.rs +++ b/apps/backend/src/providers/music_brainz.rs @@ -1,25 +1,22 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; -use convert_case::{Case, Casing}; use itertools::Itertools; use serde::{Deserialize, Serialize}; use surf::{http::headers::USER_AGENT, Client}; use crate::{ config::MusicBrainzConfig, - migrator::{MetadataImageLot, MetadataLot, MetadataSource}, + migrator::MetadataLot, models::{ - media::{ - BookSpecifics, MediaDetails, MediaSearchItem, MediaSpecifics, MetadataCreator, - MetadataImage, MetadataImageUrl, - }, + media::{MediaDetails, MediaSearchItem}, SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, utils::{convert_date_to_year, get_base_http_client, PAGE_LIMIT}, }; -pub static URL: &str = "https://www.googleapis.com/books/v1/volumes/"; +pub static URL: &str = "https://musicbrainz.org/ws/2/"; +pub static IMAGES_URL: &str = "https://coverartarchive.org/release"; #[derive(Debug, Clone)] pub struct MusicBrainzService { @@ -48,51 +45,25 @@ impl MusicBrainzService { } #[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -struct ImageLinks { - extra_large: Option, - large: Option, - medium: Option, - small: Option, - small_thumbnail: Option, - thumbnail: Option, -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -struct ItemVolumeInfo { - title: String, - published_date: Option, - image_links: Option, - description: Option, - authors: Option>, - publisher: Option, - main_category: Option, - categories: Option>, - page_count: Option, -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "kebab-case")] struct ItemResponse { id: String, - volume_info: ItemVolumeInfo, + title: String, + date: Option, + status: Option, } #[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "kebab-case")] struct SearchResponse { - total_items: i32, - items: Option>, + count: i32, + releases: Vec, } #[async_trait] impl MediaProvider for MusicBrainzService { async fn details(&self, identifier: &str) -> Result { - let mut rsp = self.client.get(identifier).await.map_err(|e| anyhow!(e))?; - let data: ItemResponse = rsp.body_json().await.map_err(|e| anyhow!(e))?; - let d = self.google_books_response_to_search_response(data.volume_info, data.id); - Ok(d) + todo!(); } async fn search( @@ -101,135 +72,39 @@ impl MediaProvider for MusicBrainzService { page: Option, ) -> Result> { let page = page.unwrap_or(1); - let index = (page - 1) * PAGE_LIMIT; let mut rsp = self .client - .get("") + .get("release") .query(&serde_json::json!({ - "q": format!("intitle:{}", query), - "maxResults": PAGE_LIMIT, - "printType": "books", - "startIndex": index + "query": format!("release:{}", query), + "limit": PAGE_LIMIT, + "offset": (page - 1) * PAGE_LIMIT, + "fmt": "json", })) .unwrap() .await .map_err(|e| anyhow!(e))?; let search: SearchResponse = rsp.body_json().await.map_err(|e| anyhow!(e))?; - let resp = search - .items - .unwrap_or_default() + let items = search + .releases .into_iter() - .map(|b| { - let MediaDetails { - identifier, - title, - lot, - images, - publish_year, - .. - } = self.google_books_response_to_search_response(b.volume_info, b.id); - let image = images - .into_iter() - .map(|i| match i.url { - MetadataImageUrl::S3(_u) => unreachable!(), - MetadataImageUrl::Url(u) => u, - }) - .collect_vec() - .get(0) - .cloned(); - MediaSearchItem { - identifier, - lot, - title, - image, - publish_year, - } + .map(|r| MediaSearchItem { + image: Some(format!("{}/{}/front", IMAGES_URL, r.id)), + identifier: r.id, + lot: MetadataLot::Music, + title: r.title, + publish_year: r.date.and_then(|d| convert_date_to_year(&d)), }) - .collect(); - let next_page = if search.total_items - ((page) * PAGE_LIMIT) > 0 { + .collect_vec(); + let next_page = if search.count - ((page) * PAGE_LIMIT) > 0 { Some(page + 1) } else { None }; Ok(SearchResults { - total: search.total_items, - items: resp, + total: search.count, + items, next_page, }) } } -impl MusicBrainzService { - fn google_books_response_to_search_response( - &self, - item: ItemVolumeInfo, - id: String, - ) -> MediaDetails { - let mut images = vec![]; - if let Some(il) = item.image_links { - if let Some(a) = il.thumbnail { - images.push(a); - } - if let Some(a) = il.small_thumbnail { - images.push(a); - } - if let Some(a) = il.small { - images.push(a); - } - if let Some(a) = il.medium { - images.push(a); - } - if let Some(a) = il.large { - images.push(a); - } - if let Some(a) = il.extra_large { - images.push(a); - } - }; - let images = images.into_iter().map(|a| MetadataImage { - url: MetadataImageUrl::Url(a), - lot: MetadataImageLot::Poster, - }); - let mut creators = item - .authors - .unwrap_or_default() - .into_iter() - .map(|a| MetadataCreator { - name: a, - role: "Author".to_owned(), - image: None, - }) - .collect_vec(); - if let Some(p) = item.publisher { - creators.push(MetadataCreator { - name: p, - role: "Publisher".to_owned(), - image: None, - }); - } - let mut genres = item - .categories - .unwrap_or_default() - .into_iter() - .flat_map(|c| c.split(" / ").map(|g| g.to_case(Case::Title)).collect_vec()) - .collect_vec(); - if let Some(g) = item.main_category { - genres.push(g); - } - MediaDetails { - identifier: id, - lot: MetadataLot::Book, - source: MetadataSource::GoogleBooks, - production_status: "Released".to_owned(), - title: item.title, - description: item.description, - creators: creators.into_iter().unique().collect(), - genres: genres.into_iter().unique().collect(), - publish_year: item.published_date.and_then(|d| convert_date_to_year(&d)), - publish_date: None, - specifics: MediaSpecifics::Book(BookSpecifics { - pages: item.page_count, - }), - images: images.unique().collect(), - } - } -} From c9bb3004654a3a2d9d685b5460ff7a1290b3552e Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 19:46:34 +0530 Subject: [PATCH 08/29] fix(frontend): precision issues --- .../pages/fitness/exercises/in-progress.tsx | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/apps/frontend/src/pages/fitness/exercises/in-progress.tsx b/apps/frontend/src/pages/fitness/exercises/in-progress.tsx index 1ec9b0620a..96fc81ed58 100644 --- a/apps/frontend/src/pages/fitness/exercises/in-progress.tsx +++ b/apps/frontend/src/pages/fitness/exercises/in-progress.tsx @@ -92,17 +92,28 @@ const StatInput = (props: { return currentWorkout ? ( { setCurrentWorkout( produce(currentWorkout, (draft) => { draft.exercises[props.exerciseIdx].sets[props.setIdx].stats[ props.stat - ] = typeof v === "number" ? v : 0; + ] = typeof v === "number" ? v : undefined; }), ); }} size="xs" styles={{ input: { width: rem(72), textAlign: "center" } }} + precision={ + typeof props.inputStep === "number" + ? Math.log10(1 / props.inputStep) + : undefined + } step={props.inputStep} hideControls required @@ -206,16 +217,16 @@ const ExerciseDisplay = (props: { - + SET {durationCol ? ( - + DURATION (MIN) ) : null} {distanceCol ? ( - + DISTANCE ( {match(userPreferences.data.fitness.exercises.distanceUnit) .with(UserDistanceUnit.Kilometer, () => "KM") @@ -225,7 +236,7 @@ const ExerciseDisplay = (props: { ) : null} {weightCol ? ( - + WEIGHT ( {match(userPreferences.data.fitness.exercises.weightUnit) .with(UserWeightUnit.Kilogram, () => "KG") @@ -235,11 +246,11 @@ const ExerciseDisplay = (props: { ) : null} {repsCol ? ( - + REPS ) : null} - + {props.exercise.sets.map((s, idx) => ( From 67f800e1ebe97f06dd8297f2c1a3a2fc940b6733 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 19:50:40 +0530 Subject: [PATCH 09/29] fix(frontend): get verb for music --- apps/frontend/src/lib/utilities.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/frontend/src/lib/utilities.ts b/apps/frontend/src/lib/utilities.ts index 66c8dd2717..d37424c599 100644 --- a/apps/frontend/src/lib/utilities.ts +++ b/apps/frontend/src/lib/utilities.ts @@ -82,6 +82,7 @@ export const getVerb = (verb: Verb, lot: MetadataLot) => { () => "watch", ) .with( + MetadataLot.Music, MetadataLot.AudioBook, MetadataLot.VideoGame, MetadataLot.Podcast, From 235b96d2c1d91d0dc19544a4d099cf14c79e293f Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 19:58:24 +0530 Subject: [PATCH 10/29] chore(backend): get summary for music --- libs/generated/src/graphql/backend/gql.ts | 4 ++-- libs/generated/src/graphql/backend/graphql.ts | 15 +++++++++++++-- .../src/backend/queries/LatestUserSummary.gql | 4 ++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/libs/generated/src/graphql/backend/gql.ts b/libs/generated/src/graphql/backend/gql.ts index f686cc774e..479f6aa638 100644 --- a/libs/generated/src/graphql/backend/gql.ts +++ b/libs/generated/src/graphql/backend/gql.ts @@ -60,7 +60,7 @@ const documents = { "query ExercisesList($input: ExercisesListInput!) {\n exercisesList(input: $input) {\n total\n nextPage\n items {\n id\n name\n lot\n equipment\n attributes {\n images\n }\n }\n }\n}": types.ExercisesListDocument, "query GetPresignedUrl($key: String!) {\n getPresignedUrl(key: $key)\n}": types.GetPresignedUrlDocument, "query ImportReports {\n importReports {\n id\n source\n startedOn\n finishedOn\n success\n details {\n import {\n total\n }\n failedItems {\n lot\n step\n identifier\n error\n }\n }\n }\n}": types.ImportReportsDocument, - "query LatestUserSummary {\n latestUserSummary {\n calculatedOn\n fitness {\n measurementsRecorded\n }\n media {\n reviewsPosted\n creatorsInteractedWith\n manga {\n chapters\n read\n }\n books {\n pages\n read\n }\n movies {\n runtime\n watched\n }\n anime {\n episodes\n watched\n }\n podcasts {\n runtime\n played\n playedEpisodes\n }\n videoGames {\n played\n }\n shows {\n runtime\n watchedEpisodes\n watchedSeasons\n watched\n }\n audioBooks {\n runtime\n played\n }\n }\n }\n}": types.LatestUserSummaryDocument, + "query LatestUserSummary {\n latestUserSummary {\n calculatedOn\n fitness {\n measurementsRecorded\n }\n media {\n reviewsPosted\n creatorsInteractedWith\n manga {\n chapters\n read\n }\n books {\n pages\n read\n }\n movies {\n runtime\n watched\n }\n music {\n runtime\n listened\n }\n anime {\n episodes\n watched\n }\n podcasts {\n runtime\n played\n playedEpisodes\n }\n videoGames {\n played\n }\n shows {\n runtime\n watchedEpisodes\n watchedSeasons\n watched\n }\n audioBooks {\n runtime\n played\n }\n }\n }\n}": types.LatestUserSummaryDocument, "query MediaDetails($metadataId: Int!) {\n mediaDetails(metadataId: $metadataId) {\n ...MediaDetailsPart\n }\n}": types.MediaDetailsDocument, "fragment MediaDetailsPart on GraphqlMediaDetails {\n title\n description\n identifier\n lot\n source\n creators {\n name\n items {\n id\n name\n image\n }\n }\n posterImages\n backdropImages\n publishYear\n publishDate\n genres\n sourceUrl\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 title\n overview\n thumbnail\n number\n runtime\n }\n totalEpisodes\n }\n showSpecifics {\n seasons {\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 videoGameSpecifics {\n platforms\n }\n}": types.MediaDetailsPartFragmentDoc, "query MediaList($input: MediaListInput!) {\n mediaList(input: $input) {\n total\n nextPage\n items {\n averageRating\n data {\n identifier\n title\n image\n publishYear\n }\n }\n }\n}": types.MediaListDocument, @@ -284,7 +284,7 @@ export function graphql(source: "query ImportReports {\n importReports {\n i /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query LatestUserSummary {\n latestUserSummary {\n calculatedOn\n fitness {\n measurementsRecorded\n }\n media {\n reviewsPosted\n creatorsInteractedWith\n manga {\n chapters\n read\n }\n books {\n pages\n read\n }\n movies {\n runtime\n watched\n }\n anime {\n episodes\n watched\n }\n podcasts {\n runtime\n played\n playedEpisodes\n }\n videoGames {\n played\n }\n shows {\n runtime\n watchedEpisodes\n watchedSeasons\n watched\n }\n audioBooks {\n runtime\n played\n }\n }\n }\n}"): (typeof documents)["query LatestUserSummary {\n latestUserSummary {\n calculatedOn\n fitness {\n measurementsRecorded\n }\n media {\n reviewsPosted\n creatorsInteractedWith\n manga {\n chapters\n read\n }\n books {\n pages\n read\n }\n movies {\n runtime\n watched\n }\n anime {\n episodes\n watched\n }\n podcasts {\n runtime\n played\n playedEpisodes\n }\n videoGames {\n played\n }\n shows {\n runtime\n watchedEpisodes\n watchedSeasons\n watched\n }\n audioBooks {\n runtime\n played\n }\n }\n }\n}"]; +export function graphql(source: "query LatestUserSummary {\n latestUserSummary {\n calculatedOn\n fitness {\n measurementsRecorded\n }\n media {\n reviewsPosted\n creatorsInteractedWith\n manga {\n chapters\n read\n }\n books {\n pages\n read\n }\n movies {\n runtime\n watched\n }\n music {\n runtime\n listened\n }\n anime {\n episodes\n watched\n }\n podcasts {\n runtime\n played\n playedEpisodes\n }\n videoGames {\n played\n }\n shows {\n runtime\n watchedEpisodes\n watchedSeasons\n watched\n }\n audioBooks {\n runtime\n played\n }\n }\n }\n}"): (typeof documents)["query LatestUserSummary {\n latestUserSummary {\n calculatedOn\n fitness {\n measurementsRecorded\n }\n media {\n reviewsPosted\n creatorsInteractedWith\n manga {\n chapters\n read\n }\n books {\n pages\n read\n }\n movies {\n runtime\n watched\n }\n music {\n runtime\n listened\n }\n anime {\n episodes\n watched\n }\n podcasts {\n runtime\n played\n playedEpisodes\n }\n videoGames {\n played\n }\n shows {\n runtime\n watchedEpisodes\n watchedSeasons\n watched\n }\n audioBooks {\n runtime\n played\n }\n }\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 5b55df0309..02e64bf1a6 100644 --- a/libs/generated/src/graphql/backend/graphql.ts +++ b/libs/generated/src/graphql/backend/graphql.ts @@ -375,6 +375,7 @@ export type GraphqlMediaDetails = { lot: MetadataLot; mangaSpecifics?: Maybe; movieSpecifics?: Maybe; + musicSpecifics?: Maybe; podcastSpecifics?: Maybe; posterImages: Array; productionStatus: Scalars['String']['output']; @@ -624,6 +625,15 @@ export type MoviesSummary = { watched: Scalars['Int']['output']; }; +export type MusicSpecifics = { + runtime?: Maybe; +}; + +export type MusicSummary = { + listened: Scalars['Int']['output']; + runtime: Scalars['Int']['output']; +}; + export type MutationRoot = { /** Add a media item to a collection if it is not there, otherwise do nothing. */ addMediaToCollection: Scalars['Boolean']['output']; @@ -1452,6 +1462,7 @@ export type UserMediaSummary = { creatorsInteractedWith: Scalars['Int']['output']; manga: MangaSummary; movies: MoviesSummary; + music: MusicSummary; podcasts: PodcastsSummary; reviewsPosted: Scalars['Int']['output']; shows: ShowsSummary; @@ -1834,7 +1845,7 @@ export type ImportReportsQuery = { importReports: Array<{ id: number, source: Im export type LatestUserSummaryQueryVariables = Exact<{ [key: string]: never; }>; -export type LatestUserSummaryQuery = { latestUserSummary: { calculatedOn: Date, fitness: { measurementsRecorded: number }, media: { reviewsPosted: number, creatorsInteractedWith: number, manga: { chapters: number, read: number }, books: { pages: number, read: number }, movies: { runtime: number, watched: number }, anime: { episodes: number, watched: number }, podcasts: { runtime: number, played: number, playedEpisodes: number }, videoGames: { played: number }, shows: { runtime: number, watchedEpisodes: number, watchedSeasons: number, watched: number }, audioBooks: { runtime: number, played: number } } } }; +export type LatestUserSummaryQuery = { latestUserSummary: { calculatedOn: Date, fitness: { measurementsRecorded: number }, media: { reviewsPosted: number, creatorsInteractedWith: number, manga: { chapters: number, read: number }, books: { pages: number, read: number }, movies: { runtime: number, watched: number }, music: { runtime: number, listened: number }, anime: { episodes: number, watched: number }, podcasts: { runtime: number, played: number, playedEpisodes: number }, videoGames: { played: number }, shows: { runtime: number, watchedEpisodes: number, watchedSeasons: number, watched: number }, audioBooks: { runtime: number, played: number } } } }; export type MediaDetailsQueryVariables = Exact<{ metadataId: Scalars['Int']['input']; @@ -1982,7 +1993,7 @@ export const ExerciseInformationDocument = {"kind":"Document","definitions":[{"k 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":"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":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"equipment"}},{"kind":"Field","name":{"kind":"Name","value":"attributes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"images"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const GetPresignedUrlDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetPresignedUrl"},"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":"getPresignedUrl"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"key"},"value":{"kind":"Variable","name":{"kind":"Name","value":"key"}}}]}]}}]} as unknown as DocumentNode; 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":"startedOn"}},{"kind":"Field","name":{"kind":"Name","value":"finishedOn"}},{"kind":"Field","name":{"kind":"Name","value":"success"}},{"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":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]}}]}}]} as unknown as DocumentNode; -export const LatestUserSummaryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LatestUserSummary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"latestUserSummary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"calculatedOn"}},{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"measurementsRecorded"}}]}},{"kind":"Field","name":{"kind":"Name","value":"media"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reviewsPosted"}},{"kind":"Field","name":{"kind":"Name","value":"creatorsInteractedWith"}},{"kind":"Field","name":{"kind":"Name","value":"manga"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chapters"}},{"kind":"Field","name":{"kind":"Name","value":"read"}}]}},{"kind":"Field","name":{"kind":"Name","value":"books"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pages"}},{"kind":"Field","name":{"kind":"Name","value":"read"}}]}},{"kind":"Field","name":{"kind":"Name","value":"movies"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"watched"}}]}},{"kind":"Field","name":{"kind":"Name","value":"anime"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodes"}},{"kind":"Field","name":{"kind":"Name","value":"watched"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcasts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"played"}},{"kind":"Field","name":{"kind":"Name","value":"playedEpisodes"}}]}},{"kind":"Field","name":{"kind":"Name","value":"videoGames"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"played"}}]}},{"kind":"Field","name":{"kind":"Name","value":"shows"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"watchedEpisodes"}},{"kind":"Field","name":{"kind":"Name","value":"watchedSeasons"}},{"kind":"Field","name":{"kind":"Name","value":"watched"}}]}},{"kind":"Field","name":{"kind":"Name","value":"audioBooks"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"played"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const LatestUserSummaryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LatestUserSummary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"latestUserSummary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"calculatedOn"}},{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"measurementsRecorded"}}]}},{"kind":"Field","name":{"kind":"Name","value":"media"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reviewsPosted"}},{"kind":"Field","name":{"kind":"Name","value":"creatorsInteractedWith"}},{"kind":"Field","name":{"kind":"Name","value":"manga"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chapters"}},{"kind":"Field","name":{"kind":"Name","value":"read"}}]}},{"kind":"Field","name":{"kind":"Name","value":"books"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pages"}},{"kind":"Field","name":{"kind":"Name","value":"read"}}]}},{"kind":"Field","name":{"kind":"Name","value":"movies"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"watched"}}]}},{"kind":"Field","name":{"kind":"Name","value":"music"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"listened"}}]}},{"kind":"Field","name":{"kind":"Name","value":"anime"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodes"}},{"kind":"Field","name":{"kind":"Name","value":"watched"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcasts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"played"}},{"kind":"Field","name":{"kind":"Name","value":"playedEpisodes"}}]}},{"kind":"Field","name":{"kind":"Name","value":"videoGames"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"played"}}]}},{"kind":"Field","name":{"kind":"Name","value":"shows"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"watchedEpisodes"}},{"kind":"Field","name":{"kind":"Name","value":"watchedSeasons"}},{"kind":"Field","name":{"kind":"Name","value":"watched"}}]}},{"kind":"Field","name":{"kind":"Name","value":"audioBooks"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"played"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const MediaDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MediaDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mediaDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"metadataId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"MediaDetailsPart"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MediaDetailsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GraphqlMediaDetails"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"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":"posterImages"}},{"kind":"Field","name":{"kind":"Name","value":"backdropImages"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}},{"kind":"Field","name":{"kind":"Name","value":"publishDate"}},{"kind":"Field","name":{"kind":"Name","value":"genres"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"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":"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":"totalEpisodes"}}]}},{"kind":"Field","name":{"kind":"Name","value":"showSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seasons"},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"videoGameSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"platforms"}}]}}]}}]} as unknown as DocumentNode; export const MediaListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MediaList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MediaListInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mediaList"},"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":"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":"averageRating"}},{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const MediaSearchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MediaSearch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"lot"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MetadataLot"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"source"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MetadataSource"}}}},{"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":"mediaSearch"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"lot"},"value":{"kind":"Variable","name":{"kind":"Name","value":"lot"}}},{"kind":"Argument","name":{"kind":"Name","value":"source"},"value":{"kind":"Variable","name":{"kind":"Name","value":"source"}}},{"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":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"databaseId"}},{"kind":"Field","name":{"kind":"Name","value":"item"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}}]}}]}}]}}]}}]} as unknown as DocumentNode; diff --git a/libs/graphql/src/backend/queries/LatestUserSummary.gql b/libs/graphql/src/backend/queries/LatestUserSummary.gql index 5c0eda9e68..9695f6f60f 100644 --- a/libs/graphql/src/backend/queries/LatestUserSummary.gql +++ b/libs/graphql/src/backend/queries/LatestUserSummary.gql @@ -19,6 +19,10 @@ query LatestUserSummary { runtime watched } + music { + runtime + listened + } anime { episodes watched From f1409aefb9775f2726921c1ae3292f45babba39c Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 20:00:25 +0530 Subject: [PATCH 11/29] feat(frontend): display music summary --- apps/frontend/src/pages/index.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/apps/frontend/src/pages/index.tsx b/apps/frontend/src/pages/index.tsx index ef767f7880..ea127b7f6c 100644 --- a/apps/frontend/src/pages/index.tsx +++ b/apps/frontend/src/pages/index.tsx @@ -237,6 +237,21 @@ const Page: NextPageWithLayout = () => { }, ]} /> + Date: Wed, 16 Aug 2023 20:55:39 +0530 Subject: [PATCH 12/29] fix(backend): get correct release data --- apps/backend/src/providers/music_brainz.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/apps/backend/src/providers/music_brainz.rs b/apps/backend/src/providers/music_brainz.rs index e593701a04..aec5fd9faf 100644 --- a/apps/backend/src/providers/music_brainz.rs +++ b/apps/backend/src/providers/music_brainz.rs @@ -16,7 +16,7 @@ use crate::{ }; pub static URL: &str = "https://musicbrainz.org/ws/2/"; -pub static IMAGES_URL: &str = "https://coverartarchive.org/release"; +pub static IMAGES_URL: &str = "https://coverartarchive.org/"; #[derive(Debug, Clone)] pub struct MusicBrainzService { @@ -46,18 +46,17 @@ impl MusicBrainzService { #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "kebab-case")] -struct ItemResponse { +struct ItemReleaseGroup { id: String, title: String, - date: Option, - status: Option, + first_release_date: Option, } #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "kebab-case")] struct SearchResponse { count: i32, - releases: Vec, + release_groups: Vec, } #[async_trait] @@ -74,7 +73,7 @@ impl MediaProvider for MusicBrainzService { let page = page.unwrap_or(1); let mut rsp = self .client - .get("release") + .get("release-group") .query(&serde_json::json!({ "query": format!("release:{}", query), "limit": PAGE_LIMIT, @@ -86,14 +85,14 @@ impl MediaProvider for MusicBrainzService { .map_err(|e| anyhow!(e))?; let search: SearchResponse = rsp.body_json().await.map_err(|e| anyhow!(e))?; let items = search - .releases + .release_groups .into_iter() .map(|r| MediaSearchItem { - image: Some(format!("{}/{}/front", IMAGES_URL, r.id)), + image: Some(format!("{}/release-group/{}/front", IMAGES_URL, r.id)), identifier: r.id, lot: MetadataLot::Music, title: r.title, - publish_year: r.date.and_then(|d| convert_date_to_year(&d)), + publish_year: r.first_release_date.and_then(|d| convert_date_to_year(&d)), }) .collect_vec(); let next_page = if search.count - ((page) * PAGE_LIMIT) > 0 { From b3a1baae68ca9760b73c441014b97dbb744bcd81 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 21:06:01 +0530 Subject: [PATCH 13/29] refactor(backend): rewrite to use LastFm instead of MusicBrainz --- apps/backend/src/config.rs | 12 ++++----- .../src/migrator/m20230410_create_metadata.rs | 4 +-- apps/backend/src/miscellaneous/resolver.rs | 20 +++++++-------- .../providers/{music_brainz.rs => last_fm.rs} | 25 ++++++++++--------- apps/backend/src/providers/mod.rs | 2 +- docs/includes/backend-config-schema.ts | 10 ++++---- 6 files changed, 37 insertions(+), 36 deletions(-) rename apps/backend/src/providers/{music_brainz.rs => last_fm.rs} (83%) diff --git a/apps/backend/src/config.rs b/apps/backend/src/config.rs index cb1e7857fa..dacad1a9dc 100644 --- a/apps/backend/src/config.rs +++ b/apps/backend/src/config.rs @@ -150,17 +150,17 @@ pub struct MovieConfig { } #[derive(Debug, Serialize, Deserialize, Clone, Config)] -#[config(rename_all = "snake_case", env_prefix = "MUSIC_BRAINZ_")] -pub struct MusicBrainzConfig { - /// Used for changing the user agent if your requests are being rate limited. - pub user_agent: Option, +#[config(rename_all = "snake_case", env_prefix = "LASTFM_")] +pub struct LastFmConfig { + /// The api key for LastFM. + pub api_key: String, } #[derive(Debug, Serialize, Deserialize, Clone, Config)] pub struct MusicConfig { - /// Settings related to Music Brainz. + /// Settings related to LastFM. #[setting(nested)] - pub music_brainz: MusicBrainzConfig, + pub last_fm: LastFmConfig, } impl IsFeatureEnabled for MovieConfig {} diff --git a/apps/backend/src/migrator/m20230410_create_metadata.rs b/apps/backend/src/migrator/m20230410_create_metadata.rs index 6589452daf..72f31b49c7 100644 --- a/apps/backend/src/migrator/m20230410_create_metadata.rs +++ b/apps/backend/src/migrator/m20230410_create_metadata.rs @@ -69,8 +69,8 @@ pub enum MetadataSource { Itunes, #[sea_orm(string_value = "LI")] Listennotes, - #[sea_orm(string_value = "MU")] - MusicBrainz, + #[sea_orm(string_value = "LM")] + LastFm, #[sea_orm(string_value = "OL")] Openlibrary, #[sea_orm(string_value = "TM")] diff --git a/apps/backend/src/miscellaneous/resolver.rs b/apps/backend/src/miscellaneous/resolver.rs index b285e59946..bfc5e3d8af 100644 --- a/apps/backend/src/miscellaneous/resolver.rs +++ b/apps/backend/src/miscellaneous/resolver.rs @@ -79,8 +79,8 @@ use crate::{ google_books::GoogleBooksService, igdb::IgdbService, itunes::ITunesService, + last_fm::LastFmService, listennotes::ListennotesService, - music_brainz::MusicBrainzService, openlibrary::OpenlibraryService, tmdb::{TmdbMovieService, TmdbService, TmdbShowService}, }, @@ -1119,7 +1119,7 @@ pub struct MiscellaneousService { pub tmdb_shows_service: TmdbShowService, pub anilist_anime_service: AnilistAnimeService, pub anilist_manga_service: AnilistMangaService, - pub music_brainz_service: MusicBrainzService, + pub last_fm_service: LastFmService, pub integration_service: IntegrationService, pub update_metadata: SqliteStorage, pub recalculate_user_summary: SqliteStorage, @@ -1148,7 +1148,7 @@ impl MiscellaneousService { let google_books_service = GoogleBooksService::new(&config.books.google_books).await; let tmdb_movies_service = TmdbMovieService::new(&config.movies.tmdb).await; let tmdb_shows_service = TmdbShowService::new(&config.shows.tmdb).await; - let music_brainz_service = MusicBrainzService::new(&config.music.music_brainz).await; + let last_fm_service = LastFmService::new(&config.music.last_fm).await; let audible_service = AudibleService::new(&config.audio_books.audible).await; let igdb_service = IgdbService::new(&config.video_games).await; let itunes_service = ITunesService::new(&config.podcasts.itunes).await; @@ -1182,7 +1182,7 @@ impl MiscellaneousService { tmdb_shows_service, anilist_anime_service, anilist_manga_service, - music_brainz_service, + last_fm_service, integration_service, update_metadata: update_metadata.clone(), recalculate_user_summary: recalculate_user_summary.clone(), @@ -1339,7 +1339,7 @@ impl MiscellaneousService { let identifier = &model.identifier; let source_url = match model.source { MetadataSource::Custom => None, - MetadataSource::MusicBrainz => todo!(), + MetadataSource::LastFm => todo!(), MetadataSource::Itunes => Some(format!( "https://podcasts.apple.com/us/podcast/{slug}/id{identifier}" )), @@ -2626,7 +2626,7 @@ impl MiscellaneousService { fn get_provider(&self, lot: MetadataLot, source: MetadataSource) -> Result { let err = || Err(Error::new("This source is not supported".to_owned())); let service: Provider = match source { - MetadataSource::MusicBrainz => Box::new(self.music_brainz_service.clone()), + MetadataSource::LastFm => Box::new(self.last_fm_service.clone()), MetadataSource::Openlibrary => Box::new(self.openlibrary_service.clone()), MetadataSource::Itunes => Box::new(self.itunes_service.clone()), MetadataSource::GoogleBooks => Box::new(self.google_books_service.clone()), @@ -4121,7 +4121,7 @@ impl MiscellaneousService { async fn media_sources_for_lot(&self, lot: MetadataLot) -> Vec { match lot { - MetadataLot::Music => vec![MetadataSource::MusicBrainz], + MetadataLot::Music => vec![MetadataSource::LastFm], MetadataLot::AudioBook => vec![MetadataSource::Audible], MetadataLot::Book => vec![MetadataSource::Openlibrary, MetadataSource::GoogleBooks], MetadataLot::Podcast => vec![MetadataSource::Itunes, MetadataSource::Listennotes], @@ -4135,9 +4135,9 @@ impl MiscellaneousService { MetadataSource::iter() .map(|source| { let (supported, default) = match source { - MetadataSource::MusicBrainz => ( - MusicBrainzService::supported_languages(), - MusicBrainzService::default_language(), + MetadataSource::LastFm => ( + LastFmService::supported_languages(), + LastFmService::default_language(), ), MetadataSource::Itunes => ( ITunesService::supported_languages(), diff --git a/apps/backend/src/providers/music_brainz.rs b/apps/backend/src/providers/last_fm.rs similarity index 83% rename from apps/backend/src/providers/music_brainz.rs rename to apps/backend/src/providers/last_fm.rs index aec5fd9faf..9733993625 100644 --- a/apps/backend/src/providers/music_brainz.rs +++ b/apps/backend/src/providers/last_fm.rs @@ -1,11 +1,12 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; +use http_types::mime; use itertools::Itertools; use serde::{Deserialize, Serialize}; -use surf::{http::headers::USER_AGENT, Client}; +use surf::{http::headers::ACCEPT, Client}; use crate::{ - config::MusicBrainzConfig, + config::LastFmConfig, migrator::MetadataLot, models::{ media::{MediaDetails, MediaSearchItem}, @@ -19,11 +20,12 @@ pub static URL: &str = "https://musicbrainz.org/ws/2/"; pub static IMAGES_URL: &str = "https://coverartarchive.org/"; #[derive(Debug, Clone)] -pub struct MusicBrainzService { +pub struct LastFmService { client: Client, + api_key: String, } -impl MediaProviderLanguages for MusicBrainzService { +impl MediaProviderLanguages for LastFmService { fn supported_languages() -> Vec { vec!["us".to_owned()] } @@ -33,14 +35,13 @@ impl MediaProviderLanguages for MusicBrainzService { } } -impl MusicBrainzService { - pub async fn new(config: &MusicBrainzConfig) -> Self { - let mut headers = vec![]; - if let Some(ref u) = config.user_agent { - headers.push((USER_AGENT, u.clone())); +impl LastFmService { + pub async fn new(config: &LastFmConfig) -> Self { + let client = get_base_http_client(URL, vec![(ACCEPT, mime::JSON)]); + Self { + client, + api_key: config.api_key.clone(), } - let client = get_base_http_client(URL, headers); - Self { client } } } @@ -60,7 +61,7 @@ struct SearchResponse { } #[async_trait] -impl MediaProvider for MusicBrainzService { +impl MediaProvider for LastFmService { async fn details(&self, identifier: &str) -> Result { todo!(); } diff --git a/apps/backend/src/providers/mod.rs b/apps/backend/src/providers/mod.rs index 497c3a0fba..52dba85b12 100644 --- a/apps/backend/src/providers/mod.rs +++ b/apps/backend/src/providers/mod.rs @@ -3,7 +3,7 @@ pub mod audible; pub mod google_books; pub mod igdb; pub mod itunes; +pub mod last_fm; pub mod listennotes; -pub mod music_brainz; pub mod openlibrary; pub mod tmdb; diff --git a/docs/includes/backend-config-schema.ts b/docs/includes/backend-config-schema.ts index ad39aab57e..f33ceb131d 100644 --- a/docs/includes/backend-config-schema.ts +++ b/docs/includes/backend-config-schema.ts @@ -132,14 +132,14 @@ export interface MovieConfig { tmdb: MoviesTmdbConfig; } -export interface MusicBrainzConfig { - /** Used for changing the user agent if your requests are being rate limited. */ - user_agent: string | null; +export interface LastFmConfig { + /** The api key for LastFM. */ + api_key: string; } export interface MusicConfig { - /** Settings related to Music Brainz. */ - musicBrainz: MusicBrainzConfig; + /** Settings related to LastFM. */ + lastFm: LastFmConfig; } export interface ITunesConfig { From 7b6404f05e69cd6d4ddd46b237362fe03d78a64c Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 21:39:37 +0530 Subject: [PATCH 14/29] fix(backend): get enabled features from resolver --- apps/backend/src/miscellaneous/resolver.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/backend/src/miscellaneous/resolver.rs b/apps/backend/src/miscellaneous/resolver.rs index bfc5e3d8af..284fd8f16f 100644 --- a/apps/backend/src/miscellaneous/resolver.rs +++ b/apps/backend/src/miscellaneous/resolver.rs @@ -2470,6 +2470,8 @@ impl MiscellaneousService { async fn user_preferences(&self, user_id: i32) -> Result { let mut prefs = self.user_by_id(user_id).await?.preferences; + prefs.features_enabled.media.music = + self.config.music.is_enabled() && prefs.features_enabled.media.music; prefs.features_enabled.media.anime = self.config.anime.is_enabled() && prefs.features_enabled.media.anime; prefs.features_enabled.media.audio_book = From 551671ee392d3559476b3552d3e7a46c79333bff Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 21:39:50 +0530 Subject: [PATCH 15/29] feat(backend): complete lastfm search response --- apps/backend/src/config.rs | 12 ++++- apps/backend/src/providers/last_fm.rs | 71 ++++++++++++++++++-------- docs/includes/backend-config-schema.ts | 2 +- 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/apps/backend/src/config.rs b/apps/backend/src/config.rs index dacad1a9dc..430a8ab764 100644 --- a/apps/backend/src/config.rs +++ b/apps/backend/src/config.rs @@ -142,6 +142,8 @@ pub struct MoviesTmdbConfig { pub locale: String, } +impl IsFeatureEnabled for MovieConfig {} + #[derive(Debug, Serialize, Deserialize, Clone, Config)] pub struct MovieConfig { /// Settings related to TMDB (movies). @@ -150,20 +152,25 @@ pub struct MovieConfig { } #[derive(Debug, Serialize, Deserialize, Clone, Config)] -#[config(rename_all = "snake_case", env_prefix = "LASTFM_")] +#[config(rename_all = "snake_case", env_prefix = "MUSIC_LAST_FM_")] pub struct LastFmConfig { /// The api key for LastFM. pub api_key: String, } #[derive(Debug, Serialize, Deserialize, Clone, Config)] +#[config(rename_all = "snake_case")] pub struct MusicConfig { /// Settings related to LastFM. #[setting(nested)] pub last_fm: LastFmConfig, } -impl IsFeatureEnabled for MovieConfig {} +impl IsFeatureEnabled for MusicConfig { + fn is_enabled(&self) -> bool { + !self.last_fm.api_key.is_empty() + } +} #[derive(Debug, Serialize, Deserialize, Clone, Config)] #[config(rename_all = "snake_case", env_prefix = "MANGA_ANILIST_")] @@ -491,6 +498,7 @@ impl AppConfig { cl.file_storage.s3_url = gt(); cl.integration.hasher_salt = gt(); cl.movies.tmdb.access_token = gt(); + cl.music.last_fm.api_key = gt(); cl.podcasts.listennotes.api_token = gt(); cl.shows.tmdb.access_token = gt(); cl.scheduler.database_url = gt(); diff --git a/apps/backend/src/providers/last_fm.rs b/apps/backend/src/providers/last_fm.rs index 9733993625..220738bbe8 100644 --- a/apps/backend/src/providers/last_fm.rs +++ b/apps/backend/src/providers/last_fm.rs @@ -13,11 +13,10 @@ use crate::{ SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, - utils::{convert_date_to_year, get_base_http_client, PAGE_LIMIT}, + utils::{get_base_http_client, PAGE_LIMIT}, }; -pub static URL: &str = "https://musicbrainz.org/ws/2/"; -pub static IMAGES_URL: &str = "https://coverartarchive.org/"; +pub static URL: &str = "https://ws.audioscrobbler.com/2.0/"; #[derive(Debug, Clone)] pub struct LastFmService { @@ -46,18 +45,39 @@ impl LastFmService { } #[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "kebab-case")] -struct ItemReleaseGroup { - id: String, - title: String, - first_release_date: Option, +struct ItemImage { + #[serde(rename = "#text")] + text: String, +} + +#[derive(Serialize, Deserialize, Debug)] +struct SearchItem { + mbid: String, + name: String, + image: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +struct Trackmatches { + track: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +struct OpenSearchData { + #[serde(rename = "opensearch:totalResults")] + count: String, +} + +#[derive(Serialize, Deserialize, Debug)] +struct SearchResponseInner { + #[serde(flatten)] + opensearch: OpenSearchData, + trackmatches: Trackmatches, } #[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "kebab-case")] struct SearchResponse { - count: i32, - release_groups: Vec, + results: SearchResponseInner, } #[async_trait] @@ -74,35 +94,42 @@ impl MediaProvider for LastFmService { let page = page.unwrap_or(1); let mut rsp = self .client - .get("release-group") + .get("") .query(&serde_json::json!({ - "query": format!("release:{}", query), + "api_key": self.api_key, + "method": "track.search", + "track": query, "limit": PAGE_LIMIT, - "offset": (page - 1) * PAGE_LIMIT, - "fmt": "json", + "page": page, + "format": "json", })) .unwrap() .await .map_err(|e| anyhow!(e))?; let search: SearchResponse = rsp.body_json().await.map_err(|e| anyhow!(e))?; let items = search - .release_groups + .results + .trackmatches + .track .into_iter() .map(|r| MediaSearchItem { - image: Some(format!("{}/release-group/{}/front", IMAGES_URL, r.id)), - identifier: r.id, + image: r.image.into_iter().nth(1).map(|i| i.text), + identifier: r.mbid, lot: MetadataLot::Music, - title: r.title, - publish_year: r.first_release_date.and_then(|d| convert_date_to_year(&d)), + title: r.name, + publish_year: None, }) .collect_vec(); - let next_page = if search.count - ((page) * PAGE_LIMIT) > 0 { + let next_page = if search.results.opensearch.count.parse::().unwrap() + - ((page) * PAGE_LIMIT) + > 0 + { Some(page + 1) } else { None }; Ok(SearchResults { - total: search.count, + total: search.results.opensearch.count.parse::().unwrap(), items, next_page, }) diff --git a/docs/includes/backend-config-schema.ts b/docs/includes/backend-config-schema.ts index f33ceb131d..a836998cdb 100644 --- a/docs/includes/backend-config-schema.ts +++ b/docs/includes/backend-config-schema.ts @@ -139,7 +139,7 @@ export interface LastFmConfig { export interface MusicConfig { /** Settings related to LastFM. */ - lastFm: LastFmConfig; + last_fm: LastFmConfig; } export interface ITunesConfig { From 7e9c78cd8980b270fd7a58dcd359214c6fde48a5 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 21:40:53 +0530 Subject: [PATCH 16/29] Revert "feat(backend): complete lastfm search response" This reverts commit 551671ee392d3559476b3552d3e7a46c79333bff. --- apps/backend/src/config.rs | 12 +---- apps/backend/src/providers/last_fm.rs | 71 +++++++++------------------ 2 files changed, 24 insertions(+), 59 deletions(-) diff --git a/apps/backend/src/config.rs b/apps/backend/src/config.rs index 430a8ab764..dacad1a9dc 100644 --- a/apps/backend/src/config.rs +++ b/apps/backend/src/config.rs @@ -142,8 +142,6 @@ pub struct MoviesTmdbConfig { pub locale: String, } -impl IsFeatureEnabled for MovieConfig {} - #[derive(Debug, Serialize, Deserialize, Clone, Config)] pub struct MovieConfig { /// Settings related to TMDB (movies). @@ -152,25 +150,20 @@ pub struct MovieConfig { } #[derive(Debug, Serialize, Deserialize, Clone, Config)] -#[config(rename_all = "snake_case", env_prefix = "MUSIC_LAST_FM_")] +#[config(rename_all = "snake_case", env_prefix = "LASTFM_")] pub struct LastFmConfig { /// The api key for LastFM. pub api_key: String, } #[derive(Debug, Serialize, Deserialize, Clone, Config)] -#[config(rename_all = "snake_case")] pub struct MusicConfig { /// Settings related to LastFM. #[setting(nested)] pub last_fm: LastFmConfig, } -impl IsFeatureEnabled for MusicConfig { - fn is_enabled(&self) -> bool { - !self.last_fm.api_key.is_empty() - } -} +impl IsFeatureEnabled for MovieConfig {} #[derive(Debug, Serialize, Deserialize, Clone, Config)] #[config(rename_all = "snake_case", env_prefix = "MANGA_ANILIST_")] @@ -498,7 +491,6 @@ impl AppConfig { cl.file_storage.s3_url = gt(); cl.integration.hasher_salt = gt(); cl.movies.tmdb.access_token = gt(); - cl.music.last_fm.api_key = gt(); cl.podcasts.listennotes.api_token = gt(); cl.shows.tmdb.access_token = gt(); cl.scheduler.database_url = gt(); diff --git a/apps/backend/src/providers/last_fm.rs b/apps/backend/src/providers/last_fm.rs index 220738bbe8..9733993625 100644 --- a/apps/backend/src/providers/last_fm.rs +++ b/apps/backend/src/providers/last_fm.rs @@ -13,10 +13,11 @@ use crate::{ SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, - utils::{get_base_http_client, PAGE_LIMIT}, + utils::{convert_date_to_year, get_base_http_client, PAGE_LIMIT}, }; -pub static URL: &str = "https://ws.audioscrobbler.com/2.0/"; +pub static URL: &str = "https://musicbrainz.org/ws/2/"; +pub static IMAGES_URL: &str = "https://coverartarchive.org/"; #[derive(Debug, Clone)] pub struct LastFmService { @@ -45,39 +46,18 @@ impl LastFmService { } #[derive(Serialize, Deserialize, Debug)] -struct ItemImage { - #[serde(rename = "#text")] - text: String, -} - -#[derive(Serialize, Deserialize, Debug)] -struct SearchItem { - mbid: String, - name: String, - image: Vec, -} - -#[derive(Serialize, Deserialize, Debug)] -struct Trackmatches { - track: Vec, -} - -#[derive(Serialize, Deserialize, Debug)] -struct OpenSearchData { - #[serde(rename = "opensearch:totalResults")] - count: String, -} - -#[derive(Serialize, Deserialize, Debug)] -struct SearchResponseInner { - #[serde(flatten)] - opensearch: OpenSearchData, - trackmatches: Trackmatches, +#[serde(rename_all = "kebab-case")] +struct ItemReleaseGroup { + id: String, + title: String, + first_release_date: Option, } #[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "kebab-case")] struct SearchResponse { - results: SearchResponseInner, + count: i32, + release_groups: Vec, } #[async_trait] @@ -94,42 +74,35 @@ impl MediaProvider for LastFmService { let page = page.unwrap_or(1); let mut rsp = self .client - .get("") + .get("release-group") .query(&serde_json::json!({ - "api_key": self.api_key, - "method": "track.search", - "track": query, + "query": format!("release:{}", query), "limit": PAGE_LIMIT, - "page": page, - "format": "json", + "offset": (page - 1) * PAGE_LIMIT, + "fmt": "json", })) .unwrap() .await .map_err(|e| anyhow!(e))?; let search: SearchResponse = rsp.body_json().await.map_err(|e| anyhow!(e))?; let items = search - .results - .trackmatches - .track + .release_groups .into_iter() .map(|r| MediaSearchItem { - image: r.image.into_iter().nth(1).map(|i| i.text), - identifier: r.mbid, + image: Some(format!("{}/release-group/{}/front", IMAGES_URL, r.id)), + identifier: r.id, lot: MetadataLot::Music, - title: r.name, - publish_year: None, + title: r.title, + publish_year: r.first_release_date.and_then(|d| convert_date_to_year(&d)), }) .collect_vec(); - let next_page = if search.results.opensearch.count.parse::().unwrap() - - ((page) * PAGE_LIMIT) - > 0 - { + let next_page = if search.count - ((page) * PAGE_LIMIT) > 0 { Some(page + 1) } else { None }; Ok(SearchResults { - total: search.results.opensearch.count.parse::().unwrap(), + total: search.count, items, next_page, }) From 71627f33db3cf1bed8328963e119664517ca8fd3 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 21:41:08 +0530 Subject: [PATCH 17/29] Revert "refactor(backend): rewrite to use LastFm instead of MusicBrainz" This reverts commit b3a1baae68ca9760b73c441014b97dbb744bcd81. --- apps/backend/src/config.rs | 12 ++++----- .../src/migrator/m20230410_create_metadata.rs | 4 +-- apps/backend/src/miscellaneous/resolver.rs | 20 +++++++-------- apps/backend/src/providers/mod.rs | 2 +- .../providers/{last_fm.rs => music_brainz.rs} | 25 +++++++++---------- docs/includes/backend-config-schema.ts | 11 +++++--- 6 files changed, 39 insertions(+), 35 deletions(-) rename apps/backend/src/providers/{last_fm.rs => music_brainz.rs} (83%) diff --git a/apps/backend/src/config.rs b/apps/backend/src/config.rs index dacad1a9dc..cb1e7857fa 100644 --- a/apps/backend/src/config.rs +++ b/apps/backend/src/config.rs @@ -150,17 +150,17 @@ pub struct MovieConfig { } #[derive(Debug, Serialize, Deserialize, Clone, Config)] -#[config(rename_all = "snake_case", env_prefix = "LASTFM_")] -pub struct LastFmConfig { - /// The api key for LastFM. - pub api_key: String, +#[config(rename_all = "snake_case", env_prefix = "MUSIC_BRAINZ_")] +pub struct MusicBrainzConfig { + /// Used for changing the user agent if your requests are being rate limited. + pub user_agent: Option, } #[derive(Debug, Serialize, Deserialize, Clone, Config)] pub struct MusicConfig { - /// Settings related to LastFM. + /// Settings related to Music Brainz. #[setting(nested)] - pub last_fm: LastFmConfig, + pub music_brainz: MusicBrainzConfig, } impl IsFeatureEnabled for MovieConfig {} diff --git a/apps/backend/src/migrator/m20230410_create_metadata.rs b/apps/backend/src/migrator/m20230410_create_metadata.rs index 72f31b49c7..6589452daf 100644 --- a/apps/backend/src/migrator/m20230410_create_metadata.rs +++ b/apps/backend/src/migrator/m20230410_create_metadata.rs @@ -69,8 +69,8 @@ pub enum MetadataSource { Itunes, #[sea_orm(string_value = "LI")] Listennotes, - #[sea_orm(string_value = "LM")] - LastFm, + #[sea_orm(string_value = "MU")] + MusicBrainz, #[sea_orm(string_value = "OL")] Openlibrary, #[sea_orm(string_value = "TM")] diff --git a/apps/backend/src/miscellaneous/resolver.rs b/apps/backend/src/miscellaneous/resolver.rs index 284fd8f16f..4942bb0c88 100644 --- a/apps/backend/src/miscellaneous/resolver.rs +++ b/apps/backend/src/miscellaneous/resolver.rs @@ -79,8 +79,8 @@ use crate::{ google_books::GoogleBooksService, igdb::IgdbService, itunes::ITunesService, - last_fm::LastFmService, listennotes::ListennotesService, + music_brainz::MusicBrainzService, openlibrary::OpenlibraryService, tmdb::{TmdbMovieService, TmdbService, TmdbShowService}, }, @@ -1119,7 +1119,7 @@ pub struct MiscellaneousService { pub tmdb_shows_service: TmdbShowService, pub anilist_anime_service: AnilistAnimeService, pub anilist_manga_service: AnilistMangaService, - pub last_fm_service: LastFmService, + pub music_brainz_service: MusicBrainzService, pub integration_service: IntegrationService, pub update_metadata: SqliteStorage, pub recalculate_user_summary: SqliteStorage, @@ -1148,7 +1148,7 @@ impl MiscellaneousService { let google_books_service = GoogleBooksService::new(&config.books.google_books).await; let tmdb_movies_service = TmdbMovieService::new(&config.movies.tmdb).await; let tmdb_shows_service = TmdbShowService::new(&config.shows.tmdb).await; - let last_fm_service = LastFmService::new(&config.music.last_fm).await; + let music_brainz_service = MusicBrainzService::new(&config.music.music_brainz).await; let audible_service = AudibleService::new(&config.audio_books.audible).await; let igdb_service = IgdbService::new(&config.video_games).await; let itunes_service = ITunesService::new(&config.podcasts.itunes).await; @@ -1182,7 +1182,7 @@ impl MiscellaneousService { tmdb_shows_service, anilist_anime_service, anilist_manga_service, - last_fm_service, + music_brainz_service, integration_service, update_metadata: update_metadata.clone(), recalculate_user_summary: recalculate_user_summary.clone(), @@ -1339,7 +1339,7 @@ impl MiscellaneousService { let identifier = &model.identifier; let source_url = match model.source { MetadataSource::Custom => None, - MetadataSource::LastFm => todo!(), + MetadataSource::MusicBrainz => todo!(), MetadataSource::Itunes => Some(format!( "https://podcasts.apple.com/us/podcast/{slug}/id{identifier}" )), @@ -2628,7 +2628,7 @@ impl MiscellaneousService { fn get_provider(&self, lot: MetadataLot, source: MetadataSource) -> Result { let err = || Err(Error::new("This source is not supported".to_owned())); let service: Provider = match source { - MetadataSource::LastFm => Box::new(self.last_fm_service.clone()), + MetadataSource::MusicBrainz => Box::new(self.music_brainz_service.clone()), MetadataSource::Openlibrary => Box::new(self.openlibrary_service.clone()), MetadataSource::Itunes => Box::new(self.itunes_service.clone()), MetadataSource::GoogleBooks => Box::new(self.google_books_service.clone()), @@ -4123,7 +4123,7 @@ impl MiscellaneousService { async fn media_sources_for_lot(&self, lot: MetadataLot) -> Vec { match lot { - MetadataLot::Music => vec![MetadataSource::LastFm], + MetadataLot::Music => vec![MetadataSource::MusicBrainz], MetadataLot::AudioBook => vec![MetadataSource::Audible], MetadataLot::Book => vec![MetadataSource::Openlibrary, MetadataSource::GoogleBooks], MetadataLot::Podcast => vec![MetadataSource::Itunes, MetadataSource::Listennotes], @@ -4137,9 +4137,9 @@ impl MiscellaneousService { MetadataSource::iter() .map(|source| { let (supported, default) = match source { - MetadataSource::LastFm => ( - LastFmService::supported_languages(), - LastFmService::default_language(), + MetadataSource::MusicBrainz => ( + MusicBrainzService::supported_languages(), + MusicBrainzService::default_language(), ), MetadataSource::Itunes => ( ITunesService::supported_languages(), diff --git a/apps/backend/src/providers/mod.rs b/apps/backend/src/providers/mod.rs index 52dba85b12..497c3a0fba 100644 --- a/apps/backend/src/providers/mod.rs +++ b/apps/backend/src/providers/mod.rs @@ -3,7 +3,7 @@ pub mod audible; pub mod google_books; pub mod igdb; pub mod itunes; -pub mod last_fm; pub mod listennotes; +pub mod music_brainz; pub mod openlibrary; pub mod tmdb; diff --git a/apps/backend/src/providers/last_fm.rs b/apps/backend/src/providers/music_brainz.rs similarity index 83% rename from apps/backend/src/providers/last_fm.rs rename to apps/backend/src/providers/music_brainz.rs index 9733993625..aec5fd9faf 100644 --- a/apps/backend/src/providers/last_fm.rs +++ b/apps/backend/src/providers/music_brainz.rs @@ -1,12 +1,11 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; -use http_types::mime; use itertools::Itertools; use serde::{Deserialize, Serialize}; -use surf::{http::headers::ACCEPT, Client}; +use surf::{http::headers::USER_AGENT, Client}; use crate::{ - config::LastFmConfig, + config::MusicBrainzConfig, migrator::MetadataLot, models::{ media::{MediaDetails, MediaSearchItem}, @@ -20,12 +19,11 @@ pub static URL: &str = "https://musicbrainz.org/ws/2/"; pub static IMAGES_URL: &str = "https://coverartarchive.org/"; #[derive(Debug, Clone)] -pub struct LastFmService { +pub struct MusicBrainzService { client: Client, - api_key: String, } -impl MediaProviderLanguages for LastFmService { +impl MediaProviderLanguages for MusicBrainzService { fn supported_languages() -> Vec { vec!["us".to_owned()] } @@ -35,13 +33,14 @@ impl MediaProviderLanguages for LastFmService { } } -impl LastFmService { - pub async fn new(config: &LastFmConfig) -> Self { - let client = get_base_http_client(URL, vec![(ACCEPT, mime::JSON)]); - Self { - client, - api_key: config.api_key.clone(), +impl MusicBrainzService { + pub async fn new(config: &MusicBrainzConfig) -> Self { + let mut headers = vec![]; + if let Some(ref u) = config.user_agent { + headers.push((USER_AGENT, u.clone())); } + let client = get_base_http_client(URL, headers); + Self { client } } } @@ -61,7 +60,7 @@ struct SearchResponse { } #[async_trait] -impl MediaProvider for LastFmService { +impl MediaProvider for MusicBrainzService { async fn details(&self, identifier: &str) -> Result { todo!(); } diff --git a/docs/includes/backend-config-schema.ts b/docs/includes/backend-config-schema.ts index a836998cdb..43900596c0 100644 --- a/docs/includes/backend-config-schema.ts +++ b/docs/includes/backend-config-schema.ts @@ -132,14 +132,19 @@ export interface MovieConfig { tmdb: MoviesTmdbConfig; } -export interface LastFmConfig { - /** The api key for LastFM. */ - api_key: string; +export interface MusicBrainzConfig { + /** Used for changing the user agent if your requests are being rate limited. */ + user_agent: string | null; } export interface MusicConfig { +<<<<<<< HEAD /** Settings related to LastFM. */ last_fm: LastFmConfig; +======= + /** Settings related to Music Brainz. */ + musicBrainz: MusicBrainzConfig; +>>>>>>> parent of b3a1baa... refactor(backend): rewrite to use LastFm instead of MusicBrainz } export interface ITunesConfig { From d6c463f2fa85d420e1cebb02e420fb9492fef4f7 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 21:42:43 +0530 Subject: [PATCH 18/29] fix(backend): correct casing for configuration params --- apps/backend/src/config.rs | 7 +++++-- docs/includes/backend-config-schema.ts | 7 +------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/backend/src/config.rs b/apps/backend/src/config.rs index cb1e7857fa..b6c78ed65f 100644 --- a/apps/backend/src/config.rs +++ b/apps/backend/src/config.rs @@ -142,6 +142,8 @@ pub struct MoviesTmdbConfig { pub locale: String, } +impl IsFeatureEnabled for MovieConfig {} + #[derive(Debug, Serialize, Deserialize, Clone, Config)] pub struct MovieConfig { /// Settings related to TMDB (movies). @@ -150,20 +152,21 @@ pub struct MovieConfig { } #[derive(Debug, Serialize, Deserialize, Clone, Config)] -#[config(rename_all = "snake_case", env_prefix = "MUSIC_BRAINZ_")] +#[config(rename_all = "snake_case", env_prefix = "MUSIC_MUSIC_BRAINZ_")] pub struct MusicBrainzConfig { /// Used for changing the user agent if your requests are being rate limited. pub user_agent: Option, } #[derive(Debug, Serialize, Deserialize, Clone, Config)] +#[config(rename_all = "snake_case")] pub struct MusicConfig { /// Settings related to Music Brainz. #[setting(nested)] pub music_brainz: MusicBrainzConfig, } -impl IsFeatureEnabled for MovieConfig {} +impl IsFeatureEnabled for MusicConfig {} #[derive(Debug, Serialize, Deserialize, Clone, Config)] #[config(rename_all = "snake_case", env_prefix = "MANGA_ANILIST_")] diff --git a/docs/includes/backend-config-schema.ts b/docs/includes/backend-config-schema.ts index 43900596c0..bc01011496 100644 --- a/docs/includes/backend-config-schema.ts +++ b/docs/includes/backend-config-schema.ts @@ -138,13 +138,8 @@ export interface MusicBrainzConfig { } export interface MusicConfig { -<<<<<<< HEAD - /** Settings related to LastFM. */ - last_fm: LastFmConfig; -======= /** Settings related to Music Brainz. */ - musicBrainz: MusicBrainzConfig; ->>>>>>> parent of b3a1baa... refactor(backend): rewrite to use LastFm instead of MusicBrainz + music_brainz: MusicBrainzConfig; } export interface ITunesConfig { From 21d02cf4a4b67f55787673762738a59eda1f1de9 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 22:16:18 +0530 Subject: [PATCH 19/29] refactor(backend): change structure of output --- apps/backend/src/miscellaneous/resolver.rs | 30 ++++++++++++------- apps/backend/src/models.rs | 8 ++++- libs/generated/src/graphql/backend/gql.ts | 4 +-- libs/generated/src/graphql/backend/graphql.ts | 11 +++++-- .../backend/queries/CollectionContents.gql | 10 ++++--- 5 files changed, 42 insertions(+), 21 deletions(-) diff --git a/apps/backend/src/miscellaneous/resolver.rs b/apps/backend/src/miscellaneous/resolver.rs index 4942bb0c88..2c97c89d91 100644 --- a/apps/backend/src/miscellaneous/resolver.rs +++ b/apps/backend/src/miscellaneous/resolver.rs @@ -61,15 +61,15 @@ use crate::{ models::{ media::{ AddMediaToCollection, AnimeSpecifics, AudioBookSpecifics, BookSpecifics, - CreateOrUpdateCollectionInput, CreatorExtraInformation, ImportOrExportItem, - ImportOrExportItemRating, ImportOrExportItemReview, ImportOrExportItemSeen, - MangaSpecifics, MediaCreatorSearchItem, MediaDetails, MediaListItem, MediaSearchItem, - MediaSearchItemResponse, MediaSpecifics, MetadataCreator, MetadataImage, - MetadataImageUrl, MetadataImages, MovieSpecifics, MusicSpecifics, PodcastSpecifics, - PostReviewInput, ProgressUpdateError, ProgressUpdateErrorVariant, ProgressUpdateInput, - ProgressUpdateResultUnion, SeenOrReviewExtraInformation, SeenPodcastExtraInformation, - SeenShowExtraInformation, ShowSpecifics, UserMediaReminder, UserSummary, - VideoGameSpecifics, Visibility, + CollectionContentResult, CreateOrUpdateCollectionInput, CreatorExtraInformation, + ImportOrExportItem, ImportOrExportItemRating, ImportOrExportItemReview, + ImportOrExportItemSeen, MangaSpecifics, MediaCreatorSearchItem, MediaDetails, + MediaListItem, MediaSearchItem, MediaSearchItemResponse, MediaSpecifics, + MetadataCreator, MetadataImage, MetadataImageUrl, MetadataImages, MovieSpecifics, + MusicSpecifics, PodcastSpecifics, PostReviewInput, ProgressUpdateError, + ProgressUpdateErrorVariant, ProgressUpdateInput, ProgressUpdateResultUnion, + SeenOrReviewExtraInformation, SeenPodcastExtraInformation, SeenShowExtraInformation, + ShowSpecifics, UserMediaReminder, UserSummary, VideoGameSpecifics, Visibility, }, IdObject, SearchInput, SearchResults, }, @@ -280,7 +280,7 @@ struct CollectionContentsInput { #[derive(Debug, SimpleObject)] struct CollectionContents { details: collection::Model, - results: SearchResults, + results: SearchResults, user: user::Model, } @@ -2860,10 +2860,18 @@ impl MiscellaneousService { publish_year: m.publish_year, }, u_t_m.map(|d| d.last_updated_on).unwrap_or_default(), + m.lot, )); } meta_data.sort_by_key(|item| item.1); - let items = meta_data.into_iter().rev().map(|a| a.0).collect(); + let items = meta_data + .into_iter() + .rev() + .map(|a| CollectionContentResult { + details: a.0, + lot: a.2, + }) + .collect(); let user = collection.find_related(User).one(&self.db).await?.unwrap(); Ok(CollectionContents { details: collection, diff --git a/apps/backend/src/models.rs b/apps/backend/src/models.rs index 32e660df9b..5c96d2dc94 100644 --- a/apps/backend/src/models.rs +++ b/apps/backend/src/models.rs @@ -33,7 +33,7 @@ pub struct SearchInput { #[derive(Serialize, Deserialize, Debug, SimpleObject, Clone)] #[graphql(concrete( name = "MediaCollectionContentsResults", - params(media::MediaSearchItem) + params(media::CollectionContentResult) ))] #[graphql(concrete(name = "MediaSearchResults", params(media::MediaSearchItemResponse)))] #[graphql(concrete( @@ -56,6 +56,12 @@ pub struct IdObject { pub mod media { use super::*; + #[derive(Debug, SimpleObject)] + pub struct CollectionContentResult { + pub details: MediaSearchItem, + pub lot: MetadataLot, + } + #[derive(Debug, Serialize, Deserialize, SimpleObject, Clone)] pub struct MediaSearchItemResponse { pub item: MediaSearchItem, diff --git a/libs/generated/src/graphql/backend/gql.ts b/libs/generated/src/graphql/backend/gql.ts index 479f6aa638..f05b7a76b1 100644 --- a/libs/generated/src/graphql/backend/gql.ts +++ b/libs/generated/src/graphql/backend/gql.ts @@ -49,7 +49,7 @@ const documents = { "mutation UpdateUser($input: UpdateUserInput!) {\n updateUser(input: $input) {\n id\n }\n}": types.UpdateUserDocument, "mutation UpdateUserPreference($input: UpdateUserPreferenceInput!) {\n updateUserPreference(input: $input)\n}": types.UpdateUserPreferenceDocument, "mutation YankIntegrationData {\n yankIntegrationData\n}": types.YankIntegrationDataDocument, - "query CollectionContents($input: CollectionContentsInput!) {\n collectionContents(input: $input) {\n user {\n name\n }\n results {\n total\n nextPage\n items {\n identifier\n lot\n title\n image\n publishYear\n }\n }\n details {\n name\n description\n visibility\n createdOn\n }\n }\n}": types.CollectionContentsDocument, + "query CollectionContents($input: CollectionContentsInput!) {\n collectionContents(input: $input) {\n user {\n name\n }\n results {\n total\n nextPage\n items {\n lot\n details {\n identifier\n title\n image\n publishYear\n }\n }\n }\n details {\n name\n description\n visibility\n createdOn\n }\n }\n}": types.CollectionContentsDocument, "query Collections($input: CollectionInput) {\n collections(input: $input) {\n id\n name\n description\n visibility\n numItems\n }\n}": types.CollectionsDocument, "query CoreDetails {\n coreDetails {\n version\n authorName\n repositoryLink\n docsLink\n defaultCredentials\n passwordChangeAllowed\n preferencesChangeAllowed\n usernameChangeAllowed\n itemDetailsHeight\n reviewsDisabled\n upgrade\n }\n}": types.CoreDetailsDocument, "query CoreEnabledFeatures {\n coreEnabledFeatures {\n fileStorage\n signupAllowed\n }\n}": types.CoreEnabledFeaturesDocument, @@ -240,7 +240,7 @@ export function graphql(source: "mutation YankIntegrationData {\n yankIntegrati /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query CollectionContents($input: CollectionContentsInput!) {\n collectionContents(input: $input) {\n user {\n name\n }\n results {\n total\n nextPage\n items {\n identifier\n lot\n title\n image\n publishYear\n }\n }\n details {\n name\n description\n visibility\n createdOn\n }\n }\n}"): (typeof documents)["query CollectionContents($input: CollectionContentsInput!) {\n collectionContents(input: $input) {\n user {\n name\n }\n results {\n total\n nextPage\n items {\n identifier\n lot\n title\n image\n publishYear\n }\n }\n details {\n name\n description\n visibility\n createdOn\n }\n }\n}"]; +export function graphql(source: "query CollectionContents($input: CollectionContentsInput!) {\n collectionContents(input: $input) {\n user {\n name\n }\n results {\n total\n nextPage\n items {\n lot\n details {\n identifier\n title\n image\n publishYear\n }\n }\n }\n details {\n name\n description\n visibility\n createdOn\n }\n }\n}"): (typeof documents)["query CollectionContents($input: CollectionContentsInput!) {\n collectionContents(input: $input) {\n user {\n name\n }\n results {\n total\n nextPage\n items {\n lot\n details {\n identifier\n title\n image\n publishYear\n }\n }\n }\n details {\n name\n description\n visibility\n createdOn\n }\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 02e64bf1a6..dfa5643ea6 100644 --- a/libs/generated/src/graphql/backend/graphql.ts +++ b/libs/generated/src/graphql/backend/graphql.ts @@ -87,6 +87,11 @@ export type Collection = { visibility: Visibility; }; +export type CollectionContentResult = { + details: MediaSearchItem; + lot: MetadataLot; +}; + export type CollectionContents = { details: Collection; results: MediaCollectionContentsResults; @@ -491,7 +496,7 @@ export type MangaSummary = { }; export type MediaCollectionContentsResults = { - items: Array; + items: Array; nextPage?: Maybe; total: Scalars['Int']['output']; }; @@ -1778,7 +1783,7 @@ export type CollectionContentsQueryVariables = Exact<{ }>; -export type CollectionContentsQuery = { collectionContents: { user: { name: string }, results: { total: number, nextPage?: number | null, items: Array<{ identifier: string, lot: MetadataLot, title: string, image?: string | null, publishYear?: number | null }> }, details: { name: string, description?: string | null, visibility: Visibility, createdOn: Date } } }; +export type CollectionContentsQuery = { collectionContents: { user: { name: string }, results: { total: number, nextPage?: number | null, items: Array<{ lot: MetadataLot, details: { identifier: string, title: string, image?: string | null, publishYear?: number | null } }> }, details: { name: string, description?: string | null, visibility: Visibility, createdOn: Date } } }; export type CollectionsQueryVariables = Exact<{ input?: InputMaybe; @@ -1982,7 +1987,7 @@ export const UpdateAllMetadataDocument = {"kind":"Document","definitions":[{"kin export const UpdateUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateUser"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateUserInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateUser"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; export const UpdateUserPreferenceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateUserPreference"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateUserPreferenceInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateUserPreference"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode; export const YankIntegrationDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"YankIntegrationData"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"yankIntegrationData"}}]}}]} as unknown as DocumentNode; -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":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"results"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}}]}}]}},{"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":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}}]}}]}}]}}]} as unknown as DocumentNode; +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":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"results"},"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":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}}]}}]}}]}},{"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":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}}]}}]}}]}}]} as unknown as DocumentNode; export const CollectionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Collections"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CollectionInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"collections"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"numItems"}}]}}]}}]} as unknown as DocumentNode; 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":"authorName"}},{"kind":"Field","name":{"kind":"Name","value":"repositoryLink"}},{"kind":"Field","name":{"kind":"Name","value":"docsLink"}},{"kind":"Field","name":{"kind":"Name","value":"defaultCredentials"}},{"kind":"Field","name":{"kind":"Name","value":"passwordChangeAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"preferencesChangeAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"usernameChangeAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"itemDetailsHeight"}},{"kind":"Field","name":{"kind":"Name","value":"reviewsDisabled"}},{"kind":"Field","name":{"kind":"Name","value":"upgrade"}}]}}]}}]} as unknown as DocumentNode; export const CoreEnabledFeaturesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CoreEnabledFeatures"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"coreEnabledFeatures"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fileStorage"}},{"kind":"Field","name":{"kind":"Name","value":"signupAllowed"}}]}}]}}]} as unknown as DocumentNode; diff --git a/libs/graphql/src/backend/queries/CollectionContents.gql b/libs/graphql/src/backend/queries/CollectionContents.gql index b25b224e04..0a23d2bb9a 100644 --- a/libs/graphql/src/backend/queries/CollectionContents.gql +++ b/libs/graphql/src/backend/queries/CollectionContents.gql @@ -7,11 +7,13 @@ query CollectionContents($input: CollectionContentsInput!) { total nextPage items { - identifier lot - title - image - publishYear + details { + identifier + title + image + publishYear + } } } details { From 805c892c6f1ac26acf6fd120876a4dfac28cfa7b Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 22:17:28 +0530 Subject: [PATCH 20/29] fix(frontend): adapt to new gql schema --- apps/frontend/src/pages/index.tsx | 6 +++--- apps/frontend/src/pages/media/collections/index.tsx | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/frontend/src/pages/index.tsx b/apps/frontend/src/pages/index.tsx index ea127b7f6c..607c1b2da3 100644 --- a/apps/frontend/src/pages/index.tsx +++ b/apps/frontend/src/pages/index.tsx @@ -197,12 +197,12 @@ const Page: NextPageWithLayout = () => { {inProgressCollection.data.results.items.map((lm) => ( ))} diff --git a/apps/frontend/src/pages/media/collections/index.tsx b/apps/frontend/src/pages/media/collections/index.tsx index 885658c28a..acc38ef140 100644 --- a/apps/frontend/src/pages/media/collections/index.tsx +++ b/apps/frontend/src/pages/media/collections/index.tsx @@ -67,12 +67,12 @@ const Page: NextPageWithLayout = () => { {collectionContents.data.results.items.map((lm) => ( ))} From d341a041c9d7044c622fe7d70c2786c238c1b238 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 22:21:09 +0530 Subject: [PATCH 21/29] feat(backend): remove lot from media search results --- apps/backend/src/miscellaneous/resolver.rs | 4 ---- apps/backend/src/models.rs | 1 - apps/backend/src/providers/anilist/mod.rs | 1 - apps/backend/src/providers/audible.rs | 1 - apps/backend/src/providers/google_books.rs | 2 -- apps/backend/src/providers/igdb.rs | 1 - apps/backend/src/providers/itunes.rs | 1 - apps/backend/src/providers/listennotes.rs | 1 - apps/backend/src/providers/music_brainz.rs | 1 - apps/backend/src/providers/openlibrary.rs | 1 - apps/backend/src/providers/tmdb.rs | 2 -- 11 files changed, 16 deletions(-) diff --git a/apps/backend/src/miscellaneous/resolver.rs b/apps/backend/src/miscellaneous/resolver.rs index 2c97c89d91..acdc4f1b55 100644 --- a/apps/backend/src/miscellaneous/resolver.rs +++ b/apps/backend/src/miscellaneous/resolver.rs @@ -1790,7 +1790,6 @@ impl MiscellaneousService { #[derive(Debug, FromQueryResult)] struct InnerMediaSearchItem { id: i32, - lot: MetadataLot, title: String, publish_year: Option, images: serde_json::Value, @@ -1855,7 +1854,6 @@ impl MiscellaneousService { let m_small = MediaListItem { data: MediaSearchItem { identifier: m.id.to_string(), - lot: m.lot, title: m.title, image: poster_images.get(0).cloned(), publish_year: m.publish_year, @@ -2854,7 +2852,6 @@ impl MiscellaneousService { meta_data.push(( MediaSearchItem { identifier: m.id.to_string(), - lot: m.lot, image: self.metadata_images(&m).await?.0.first().cloned(), title: m.title, publish_year: m.publish_year, @@ -4722,7 +4719,6 @@ impl MiscellaneousService { }; let metadata = MediaSearchItem { identifier: m.id.to_string(), - lot: m.lot, title: m.title, publish_year: m.publish_year, image, diff --git a/apps/backend/src/models.rs b/apps/backend/src/models.rs index 5c96d2dc94..16d0cab6dc 100644 --- a/apps/backend/src/models.rs +++ b/apps/backend/src/models.rs @@ -307,7 +307,6 @@ pub mod media { #[derive(Debug, Serialize, Deserialize, SimpleObject, Clone)] pub struct MediaSearchItem { pub identifier: String, - pub lot: MetadataLot, pub title: String, pub image: Option, pub publish_year: Option, diff --git a/apps/backend/src/providers/anilist/mod.rs b/apps/backend/src/providers/anilist/mod.rs index ddd397cde0..a66c722430 100644 --- a/apps/backend/src/providers/anilist/mod.rs +++ b/apps/backend/src/providers/anilist/mod.rs @@ -283,7 +283,6 @@ mod utils { .flatten() .map(|b| MediaSearchItem { identifier: b.id.to_string(), - lot: MetadataLot::Anime, title: b.title.unwrap().user_preferred.unwrap(), image: b.banner_image, publish_year: b diff --git a/apps/backend/src/providers/audible.rs b/apps/backend/src/providers/audible.rs index 93af860a2a..c887f8f49a 100644 --- a/apps/backend/src/providers/audible.rs +++ b/apps/backend/src/providers/audible.rs @@ -170,7 +170,6 @@ impl MediaProvider for AudibleService { let a = self.audible_response_to_search_response(d); MediaSearchItem { identifier: a.identifier, - lot: MetadataLot::AudioBook, title: a.title, image: a .images diff --git a/apps/backend/src/providers/google_books.rs b/apps/backend/src/providers/google_books.rs index 8814e7e5ba..d631460313 100644 --- a/apps/backend/src/providers/google_books.rs +++ b/apps/backend/src/providers/google_books.rs @@ -120,7 +120,6 @@ impl MediaProvider for GoogleBooksService { let MediaDetails { identifier, title, - lot, images, publish_year, .. @@ -136,7 +135,6 @@ impl MediaProvider for GoogleBooksService { .cloned(); MediaSearchItem { identifier, - lot, title, image, publish_year, diff --git a/apps/backend/src/providers/igdb.rs b/apps/backend/src/providers/igdb.rs index c5a90d7939..c3706e6ee9 100644 --- a/apps/backend/src/providers/igdb.rs +++ b/apps/backend/src/providers/igdb.rs @@ -162,7 +162,6 @@ offset: {offset}; let a = self.igdb_response_to_search_response(r); MediaSearchItem { identifier: a.identifier, - lot: MetadataLot::VideoGame, title: a.title, image: a .images diff --git a/apps/backend/src/providers/itunes.rs b/apps/backend/src/providers/itunes.rs index 69da6aefcb..99455a9adf 100644 --- a/apps/backend/src/providers/itunes.rs +++ b/apps/backend/src/providers/itunes.rs @@ -235,7 +235,6 @@ fn get_search_response(item: ITunesItem) -> MediaSearchItem { let publish_year = date.map(|d| d.year()); MediaSearchItem { identifier: item.collection_id.to_string(), - lot: MetadataLot::Podcast, title: item.collection_name, image: images.get(0).cloned(), publish_year, diff --git a/apps/backend/src/providers/listennotes.rs b/apps/backend/src/providers/listennotes.rs index bc9f828bff..d9991a19be 100644 --- a/apps/backend/src/providers/listennotes.rs +++ b/apps/backend/src/providers/listennotes.rs @@ -126,7 +126,6 @@ impl MediaProvider for ListennotesService { .into_iter() .map(|r| MediaSearchItem { identifier: r.id, - lot: MetadataLot::Podcast, title: r.title_original, image: r.image, publish_year: r.publish_date.map(|r| r.year()), diff --git a/apps/backend/src/providers/music_brainz.rs b/apps/backend/src/providers/music_brainz.rs index aec5fd9faf..38572bd612 100644 --- a/apps/backend/src/providers/music_brainz.rs +++ b/apps/backend/src/providers/music_brainz.rs @@ -90,7 +90,6 @@ impl MediaProvider for MusicBrainzService { .map(|r| MediaSearchItem { image: Some(format!("{}/release-group/{}/front", IMAGES_URL, r.id)), identifier: r.id, - lot: MetadataLot::Music, title: r.title, publish_year: r.first_release_date.and_then(|d| convert_date_to_year(&d)), }) diff --git a/apps/backend/src/providers/openlibrary.rs b/apps/backend/src/providers/openlibrary.rs index 80d8456609..c0f2a3514c 100644 --- a/apps/backend/src/providers/openlibrary.rs +++ b/apps/backend/src/providers/openlibrary.rs @@ -362,7 +362,6 @@ impl MediaProvider for OpenlibraryService { .into_iter() .map(|b| MediaSearchItem { identifier: b.identifier, - lot: MetadataLot::Book, title: b.title, image: b.images.get(0).cloned(), publish_year: b.publish_year, diff --git a/apps/backend/src/providers/tmdb.rs b/apps/backend/src/providers/tmdb.rs index 8582f7bd5e..7cf6488eb2 100644 --- a/apps/backend/src/providers/tmdb.rs +++ b/apps/backend/src/providers/tmdb.rs @@ -243,7 +243,6 @@ impl MediaProvider for TmdbMovieService { .into_iter() .map(|d| MediaSearchItem { identifier: d.id.to_string(), - lot: MetadataLot::Movie, title: d.title, publish_year: convert_date_to_year(&d.release_date), image: d.poster_path.map(|p| self.base.get_cover_image_url(p)), @@ -534,7 +533,6 @@ impl MediaProvider for TmdbShowService { .into_iter() .map(|d| MediaSearchItem { identifier: d.id.to_string(), - lot: MetadataLot::Show, title: d.name, publish_year: convert_date_to_year(&d.first_air_date), image: d.poster_path.map(|p| self.base.get_cover_image_url(p)), From 6bbb0350f773ee1874b849b1ad170ffae4830b44 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 22:25:06 +0530 Subject: [PATCH 22/29] feat(backend): return correct objects for creators --- apps/backend/src/miscellaneous/resolver.rs | 17 ++++++++++------- apps/backend/src/models.rs | 2 +- libs/generated/src/graphql/backend/gql.ts | 4 ++-- libs/generated/src/graphql/backend/graphql.ts | 7 +++---- .../src/backend/queries/CreatorDetails.gql | 10 ++++++---- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/apps/backend/src/miscellaneous/resolver.rs b/apps/backend/src/miscellaneous/resolver.rs index acdc4f1b55..8733d1b2f4 100644 --- a/apps/backend/src/miscellaneous/resolver.rs +++ b/apps/backend/src/miscellaneous/resolver.rs @@ -336,7 +336,7 @@ struct CreatorDetailsGroupedByRole { /// The name of the role performed. name: String, /// The media items in which this role was performed. - items: Vec, + items: Vec, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -4709,7 +4709,7 @@ impl MiscellaneousService { .find_also_related(Metadata) .all(&self.db) .await?; - let mut contents: HashMap> = HashMap::new(); + let mut contents: HashMap> = HashMap::new(); for (assoc, metadata) in associations { let m = metadata.unwrap(); let image = if let Some(i) = m.images.0.first() { @@ -4717,11 +4717,14 @@ impl MiscellaneousService { } else { None }; - let metadata = MediaSearchItem { - identifier: m.id.to_string(), - title: m.title, - publish_year: m.publish_year, - image, + let metadata = CollectionContentResult { + details: MediaSearchItem { + identifier: m.id.to_string(), + title: m.title, + publish_year: m.publish_year, + image, + }, + lot: m.lot, }; contents .entry(assoc.role) diff --git a/apps/backend/src/models.rs b/apps/backend/src/models.rs index 16d0cab6dc..0c55008f9f 100644 --- a/apps/backend/src/models.rs +++ b/apps/backend/src/models.rs @@ -56,7 +56,7 @@ pub struct IdObject { pub mod media { use super::*; - #[derive(Debug, SimpleObject)] + #[derive(Debug, SimpleObject, Serialize, Deserialize, Clone)] pub struct CollectionContentResult { pub details: MediaSearchItem, pub lot: MetadataLot, diff --git a/libs/generated/src/graphql/backend/gql.ts b/libs/generated/src/graphql/backend/gql.ts index f05b7a76b1..7d84ee4aa7 100644 --- a/libs/generated/src/graphql/backend/gql.ts +++ b/libs/generated/src/graphql/backend/gql.ts @@ -53,7 +53,7 @@ const documents = { "query Collections($input: CollectionInput) {\n collections(input: $input) {\n id\n name\n description\n visibility\n numItems\n }\n}": types.CollectionsDocument, "query CoreDetails {\n coreDetails {\n version\n authorName\n repositoryLink\n docsLink\n defaultCredentials\n passwordChangeAllowed\n preferencesChangeAllowed\n usernameChangeAllowed\n itemDetailsHeight\n reviewsDisabled\n upgrade\n }\n}": types.CoreDetailsDocument, "query CoreEnabledFeatures {\n coreEnabledFeatures {\n fileStorage\n signupAllowed\n }\n}": types.CoreEnabledFeaturesDocument, - "query CreatorDetails($creatorId: Int!) {\n creatorDetails(creatorId: $creatorId) {\n details {\n id\n name\n image\n }\n contents {\n name\n items {\n identifier\n lot\n title\n image\n publishYear\n }\n }\n }\n}": types.CreatorDetailsDocument, + "query CreatorDetails($creatorId: Int!) {\n creatorDetails(creatorId: $creatorId) {\n details {\n id\n name\n image\n }\n contents {\n name\n items {\n lot\n details {\n identifier\n title\n image\n publishYear\n }\n }\n }\n }\n}": types.CreatorDetailsDocument, "query CreatorsList($input: SearchInput!) {\n creatorsList(input: $input) {\n total\n nextPage\n items {\n id\n name\n image\n mediaCount\n }\n }\n}": types.CreatorsListDocument, "query Exercise($exerciseId: Int!) {\n exercise(exerciseId: $exerciseId) {\n name\n lot\n }\n}": types.ExerciseDocument, "query ExerciseInformation {\n exerciseInformation {\n filters {\n type\n level\n force\n mechanic\n equipment\n muscle\n }\n downloadRequired\n }\n}": types.ExerciseInformationDocument, @@ -256,7 +256,7 @@ export function graphql(source: "query CoreEnabledFeatures {\n coreEnabledFeatu /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query CreatorDetails($creatorId: Int!) {\n creatorDetails(creatorId: $creatorId) {\n details {\n id\n name\n image\n }\n contents {\n name\n items {\n identifier\n lot\n title\n image\n publishYear\n }\n }\n }\n}"): (typeof documents)["query CreatorDetails($creatorId: Int!) {\n creatorDetails(creatorId: $creatorId) {\n details {\n id\n name\n image\n }\n contents {\n name\n items {\n identifier\n lot\n title\n image\n publishYear\n }\n }\n }\n}"]; +export function graphql(source: "query CreatorDetails($creatorId: Int!) {\n creatorDetails(creatorId: $creatorId) {\n details {\n id\n name\n image\n }\n contents {\n name\n items {\n lot\n details {\n identifier\n title\n image\n publishYear\n }\n }\n }\n }\n}"): (typeof documents)["query CreatorDetails($creatorId: Int!) {\n creatorDetails(creatorId: $creatorId) {\n details {\n id\n name\n image\n }\n contents {\n name\n items {\n lot\n details {\n identifier\n title\n image\n publishYear\n }\n }\n }\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 dfa5643ea6..b2a13bed09 100644 --- a/libs/generated/src/graphql/backend/graphql.ts +++ b/libs/generated/src/graphql/backend/graphql.ts @@ -203,7 +203,7 @@ export type CreatorDetails = { export type CreatorDetailsGroupedByRole = { /** The media items in which this role was performed. */ - items: Array; + items: Array; /** The name of the role performed. */ name: Scalars['String']['output']; }; @@ -553,7 +553,6 @@ export type MediaListResults = { export type MediaSearchItem = { identifier: Scalars['String']['output']; image?: Maybe; - lot: MetadataLot; publishYear?: Maybe; title: Scalars['String']['output']; }; @@ -1807,7 +1806,7 @@ export type CreatorDetailsQueryVariables = Exact<{ }>; -export type CreatorDetailsQuery = { creatorDetails: { details: { id: number, name: string, image?: string | null }, contents: Array<{ name: string, items: Array<{ identifier: string, lot: MetadataLot, title: string, image?: string | null, publishYear?: number | null }> }> } }; +export type CreatorDetailsQuery = { creatorDetails: { details: { id: number, name: string, image?: string | null }, contents: Array<{ name: string, items: Array<{ lot: MetadataLot, details: { identifier: string, title: string, image?: string | null, publishYear?: number | null } }> }> } }; export type CreatorsListQueryVariables = Exact<{ input: SearchInput; @@ -1991,7 +1990,7 @@ export const CollectionContentsDocument = {"kind":"Document","definitions":[{"ki export const CollectionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Collections"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CollectionInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"collections"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"numItems"}}]}}]}}]} as unknown as DocumentNode; 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":"authorName"}},{"kind":"Field","name":{"kind":"Name","value":"repositoryLink"}},{"kind":"Field","name":{"kind":"Name","value":"docsLink"}},{"kind":"Field","name":{"kind":"Name","value":"defaultCredentials"}},{"kind":"Field","name":{"kind":"Name","value":"passwordChangeAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"preferencesChangeAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"usernameChangeAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"itemDetailsHeight"}},{"kind":"Field","name":{"kind":"Name","value":"reviewsDisabled"}},{"kind":"Field","name":{"kind":"Name","value":"upgrade"}}]}}]}}]} as unknown as DocumentNode; export const CoreEnabledFeaturesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CoreEnabledFeatures"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"coreEnabledFeatures"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fileStorage"}},{"kind":"Field","name":{"kind":"Name","value":"signupAllowed"}}]}}]}}]} as unknown as DocumentNode; -export const CreatorDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CreatorDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"creatorId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"creatorDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"creatorId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"creatorId"}}}],"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":"image"}}]}},{"kind":"Field","name":{"kind":"Name","value":"contents"},"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":"identifier"}},{"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; +export const CreatorDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CreatorDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"creatorId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"creatorDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"creatorId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"creatorId"}}}],"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":"image"}}]}},{"kind":"Field","name":{"kind":"Name","value":"contents"},"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":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const CreatorsListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CreatorsList"},"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":"creatorsList"},"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":"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":"image"}},{"kind":"Field","name":{"kind":"Name","value":"mediaCount"}}]}}]}}]}}]} as unknown as DocumentNode; export const ExerciseDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Exercise"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"exerciseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"exercise"},"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":"name"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}}]}}]}}]} as unknown as DocumentNode; export const ExerciseInformationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ExerciseInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"exerciseInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"downloadRequired"}}]}}]}}]} as unknown as DocumentNode; diff --git a/libs/graphql/src/backend/queries/CreatorDetails.gql b/libs/graphql/src/backend/queries/CreatorDetails.gql index 3000ab747b..ca3332ebbd 100644 --- a/libs/graphql/src/backend/queries/CreatorDetails.gql +++ b/libs/graphql/src/backend/queries/CreatorDetails.gql @@ -8,11 +8,13 @@ query CreatorDetails($creatorId: Int!) { contents { name items { - identifier lot - title - image - publishYear + details { + identifier + title + image + publishYear + } } } } From b5a1b352e2738822a94ca80a607f352bbefaedc2 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 22:27:04 +0530 Subject: [PATCH 23/29] refactor(backend): change type names --- apps/backend/src/miscellaneous/resolver.rs | 28 +++++++++---------- apps/backend/src/models.rs | 4 +-- libs/generated/src/graphql/backend/graphql.ts | 14 +++++----- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/apps/backend/src/miscellaneous/resolver.rs b/apps/backend/src/miscellaneous/resolver.rs index 8733d1b2f4..064e99b520 100644 --- a/apps/backend/src/miscellaneous/resolver.rs +++ b/apps/backend/src/miscellaneous/resolver.rs @@ -61,15 +61,15 @@ use crate::{ models::{ media::{ AddMediaToCollection, AnimeSpecifics, AudioBookSpecifics, BookSpecifics, - CollectionContentResult, CreateOrUpdateCollectionInput, CreatorExtraInformation, - ImportOrExportItem, ImportOrExportItemRating, ImportOrExportItemReview, - ImportOrExportItemSeen, MangaSpecifics, MediaCreatorSearchItem, MediaDetails, - MediaListItem, MediaSearchItem, MediaSearchItemResponse, MediaSpecifics, - MetadataCreator, MetadataImage, MetadataImageUrl, MetadataImages, MovieSpecifics, - MusicSpecifics, PodcastSpecifics, PostReviewInput, ProgressUpdateError, - ProgressUpdateErrorVariant, ProgressUpdateInput, ProgressUpdateResultUnion, - SeenOrReviewExtraInformation, SeenPodcastExtraInformation, SeenShowExtraInformation, - ShowSpecifics, UserMediaReminder, UserSummary, VideoGameSpecifics, Visibility, + CreateOrUpdateCollectionInput, CreatorExtraInformation, ImportOrExportItem, + ImportOrExportItemRating, ImportOrExportItemReview, ImportOrExportItemSeen, + MangaSpecifics, MediaCreatorSearchItem, MediaDetails, MediaListItem, MediaSearchItem, + MediaSearchItemResponse, MediaSearchItemWithLot, MediaSpecifics, MetadataCreator, + MetadataImage, MetadataImageUrl, MetadataImages, MovieSpecifics, MusicSpecifics, + PodcastSpecifics, PostReviewInput, ProgressUpdateError, ProgressUpdateErrorVariant, + ProgressUpdateInput, ProgressUpdateResultUnion, SeenOrReviewExtraInformation, + SeenPodcastExtraInformation, SeenShowExtraInformation, ShowSpecifics, + UserMediaReminder, UserSummary, VideoGameSpecifics, Visibility, }, IdObject, SearchInput, SearchResults, }, @@ -280,7 +280,7 @@ struct CollectionContentsInput { #[derive(Debug, SimpleObject)] struct CollectionContents { details: collection::Model, - results: SearchResults, + results: SearchResults, user: user::Model, } @@ -336,7 +336,7 @@ struct CreatorDetailsGroupedByRole { /// The name of the role performed. name: String, /// The media items in which this role was performed. - items: Vec, + items: Vec, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -2864,7 +2864,7 @@ impl MiscellaneousService { let items = meta_data .into_iter() .rev() - .map(|a| CollectionContentResult { + .map(|a| MediaSearchItemWithLot { details: a.0, lot: a.2, }) @@ -4709,7 +4709,7 @@ impl MiscellaneousService { .find_also_related(Metadata) .all(&self.db) .await?; - let mut contents: HashMap> = HashMap::new(); + let mut contents: HashMap<_, Vec<_>> = HashMap::new(); for (assoc, metadata) in associations { let m = metadata.unwrap(); let image = if let Some(i) = m.images.0.first() { @@ -4717,7 +4717,7 @@ impl MiscellaneousService { } else { None }; - let metadata = CollectionContentResult { + let metadata = MediaSearchItemWithLot { details: MediaSearchItem { identifier: m.id.to_string(), title: m.title, diff --git a/apps/backend/src/models.rs b/apps/backend/src/models.rs index 0c55008f9f..b7d61c33b7 100644 --- a/apps/backend/src/models.rs +++ b/apps/backend/src/models.rs @@ -33,7 +33,7 @@ pub struct SearchInput { #[derive(Serialize, Deserialize, Debug, SimpleObject, Clone)] #[graphql(concrete( name = "MediaCollectionContentsResults", - params(media::CollectionContentResult) + params(media::MediaSearchItemWithLot) ))] #[graphql(concrete(name = "MediaSearchResults", params(media::MediaSearchItemResponse)))] #[graphql(concrete( @@ -57,7 +57,7 @@ pub mod media { use super::*; #[derive(Debug, SimpleObject, Serialize, Deserialize, Clone)] - pub struct CollectionContentResult { + pub struct MediaSearchItemWithLot { pub details: MediaSearchItem, pub lot: MetadataLot, } diff --git a/libs/generated/src/graphql/backend/graphql.ts b/libs/generated/src/graphql/backend/graphql.ts index b2a13bed09..c7fc05041d 100644 --- a/libs/generated/src/graphql/backend/graphql.ts +++ b/libs/generated/src/graphql/backend/graphql.ts @@ -87,11 +87,6 @@ export type Collection = { visibility: Visibility; }; -export type CollectionContentResult = { - details: MediaSearchItem; - lot: MetadataLot; -}; - export type CollectionContents = { details: Collection; results: MediaCollectionContentsResults; @@ -203,7 +198,7 @@ export type CreatorDetails = { export type CreatorDetailsGroupedByRole = { /** The media items in which this role was performed. */ - items: Array; + items: Array; /** The name of the role performed. */ name: Scalars['String']['output']; }; @@ -496,7 +491,7 @@ export type MangaSummary = { }; export type MediaCollectionContentsResults = { - items: Array; + items: Array; nextPage?: Maybe; total: Scalars['Int']['output']; }; @@ -562,6 +557,11 @@ export type MediaSearchItemResponse = { item: MediaSearchItem; }; +export type MediaSearchItemWithLot = { + details: MediaSearchItem; + lot: MetadataLot; +}; + export type MediaSearchResults = { items: Array; nextPage?: Maybe; From 797f7a4ecfbdc22f10ffd683adcc49008a1a70c3 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Wed, 16 Aug 2023 22:28:03 +0530 Subject: [PATCH 24/29] chore(frontend): adapt to new gql schema --- apps/frontend/src/pages/media/people/index.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/frontend/src/pages/media/people/index.tsx b/apps/frontend/src/pages/media/people/index.tsx index b7750a2d71..67d5ac51ba 100644 --- a/apps/frontend/src/pages/media/people/index.tsx +++ b/apps/frontend/src/pages/media/people/index.tsx @@ -127,22 +127,22 @@ const Page: NextPageWithLayout = () => { > {role.items.map((item) => ( - + { lineClamp={1} mt={4} > - {item.title} + {item.details.title} From 696f8ca8d84715cc26ed47061dad3fdfd3da7adf Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Thu, 17 Aug 2023 09:24:42 +0530 Subject: [PATCH 25/29] refactor(backend): group search details together --- apps/backend/src/fitness/exercise/resolver.rs | 5 +-- apps/backend/src/miscellaneous/resolver.rs | 42 ++++++++++--------- apps/backend/src/models.rs | 9 +++- apps/backend/src/providers/anilist/mod.rs | 11 ++--- apps/backend/src/providers/audible.rs | 8 ++-- apps/backend/src/providers/google_books.rs | 8 ++-- apps/backend/src/providers/igdb.rs | 8 ++-- apps/backend/src/providers/itunes.rs | 8 ++-- apps/backend/src/providers/listennotes.rs | 5 +-- apps/backend/src/providers/music_brainz.rs | 9 ++-- apps/backend/src/providers/openlibrary.rs | 8 ++-- apps/backend/src/providers/tmdb.rs | 14 ++++--- libs/generated/src/graphql/backend/gql.ts | 20 ++++----- libs/generated/src/graphql/backend/graphql.ts | 40 +++++++++--------- .../backend/queries/CollectionContents.gql | 6 ++- .../src/backend/queries/CreatorsList.gql | 6 ++- .../src/backend/queries/ExercisesList.gql | 6 ++- .../graphql/src/backend/queries/MediaList.gql | 6 ++- .../src/backend/queries/MediaSearch.gql | 6 ++- 19 files changed, 129 insertions(+), 96 deletions(-) diff --git a/apps/backend/src/fitness/exercise/resolver.rs b/apps/backend/src/fitness/exercise/resolver.rs index d7c6e0d2f0..fb4f379174 100644 --- a/apps/backend/src/fitness/exercise/resolver.rs +++ b/apps/backend/src/fitness/exercise/resolver.rs @@ -27,7 +27,7 @@ use crate::{ Exercise as GithubExercise, ExerciseAttributes, ExerciseCategory, ExerciseMuscles, GithubExerciseAttributes, }, - SearchResults, + SearchDetails, SearchResults, }, traits::AuthProvider, utils::{get_case_insensitive_like_query, MemoryDatabase, PAGE_LIMIT}, @@ -284,9 +284,8 @@ impl ExerciseService { None }; Ok(SearchResults { - total, + details: SearchDetails { total, next_page }, items, - next_page, }) } diff --git a/apps/backend/src/miscellaneous/resolver.rs b/apps/backend/src/miscellaneous/resolver.rs index 064e99b520..ae9b918a0b 100644 --- a/apps/backend/src/miscellaneous/resolver.rs +++ b/apps/backend/src/miscellaneous/resolver.rs @@ -71,7 +71,7 @@ use crate::{ SeenPodcastExtraInformation, SeenShowExtraInformation, ShowSpecifics, UserMediaReminder, UserSummary, VideoGameSpecifics, Visibility, }, - IdObject, SearchInput, SearchResults, + IdObject, SearchDetails, SearchInput, SearchResults, }, providers::{ anilist::{AnilistAnimeService, AnilistMangaService, AnilistService}, @@ -1868,9 +1868,8 @@ impl MiscellaneousService { None }; Ok(SearchResults { - total, + details: SearchDetails { next_page, total }, items, - next_page, }) } @@ -2510,9 +2509,11 @@ impl MiscellaneousService { if let Some(q) = input.query { if q.is_empty() { return Ok(SearchResults { - total: 0, + details: SearchDetails { + total: 0, + next_page: None, + }, items: vec![], - next_page: None, }); } let provider = self.get_provider(lot, source)?; @@ -2598,9 +2599,8 @@ impl MiscellaneousService { .collect() }; let results = SearchResults { - total: results.total, + details: results.details, items: data, - next_page: results.next_page, }; Ok(results) } else { @@ -2873,13 +2873,15 @@ impl MiscellaneousService { Ok(CollectionContents { details: collection, results: SearchResults { - total: number_of_items.try_into().unwrap(), - items, - next_page: if page < number_of_pages { - Some((page + 1).try_into().unwrap()) - } else { - None + details: SearchDetails { + total: number_of_items.try_into().unwrap(), + next_page: if page < number_of_pages { + Some((page + 1).try_into().unwrap()) + } else { + None + }, }, + items, }, user, }) @@ -4689,13 +4691,15 @@ impl MiscellaneousService { creators.push(c); } Ok(SearchResults { - total: number_of_items.try_into().unwrap(), - items: creators, - next_page: if page < number_of_pages { - Some((page + 1).try_into().unwrap()) - } else { - None + details: SearchDetails { + total: number_of_items.try_into().unwrap(), + next_page: if page < number_of_pages { + Some((page + 1).try_into().unwrap()) + } else { + None + }, }, + items: creators, }) } diff --git a/apps/backend/src/models.rs b/apps/backend/src/models.rs index b7d61c33b7..2935baca40 100644 --- a/apps/backend/src/models.rs +++ b/apps/backend/src/models.rs @@ -30,6 +30,12 @@ pub struct SearchInput { pub page: Option, } +#[derive(Serialize, Deserialize, Debug, SimpleObject, Clone)] +pub struct SearchDetails { + pub total: i32, + pub next_page: Option, +} + #[derive(Serialize, Deserialize, Debug, SimpleObject, Clone)] #[graphql(concrete( name = "MediaCollectionContentsResults", @@ -43,9 +49,8 @@ pub struct SearchInput { #[graphql(concrete(name = "MediaListResults", params(media::MediaListItem)))] #[graphql(concrete(name = "ExerciseSearchResults", params(ExerciseModel)))] pub struct SearchResults { - pub total: i32, + pub details: SearchDetails, pub items: Vec, - pub next_page: Option, } #[derive(Debug, SimpleObject, Serialize, Deserialize)] diff --git a/apps/backend/src/providers/anilist/mod.rs b/apps/backend/src/providers/anilist/mod.rs index a66c722430..8b236b859e 100644 --- a/apps/backend/src/providers/anilist/mod.rs +++ b/apps/backend/src/providers/anilist/mod.rs @@ -6,8 +6,11 @@ use surf::Client; use crate::{ config::{AnimeAnilistConfig, MangaAnilistConfig}, migrator::MetadataLot, - models::media::{MediaDetails, MediaSearchItem}, models::SearchResults, + models::{ + media::{MediaDetails, MediaSearchItem}, + SearchDetails, + }, traits::{MediaProvider, MediaProviderLanguages}, utils::PAGE_LIMIT, }; @@ -79,8 +82,7 @@ impl MediaProvider for AnilistAnimeService { ) .await?; Ok(SearchResults { - total, - next_page, + details: SearchDetails { total, next_page }, items, }) } @@ -120,8 +122,7 @@ impl MediaProvider for AnilistMangaService { ) .await?; Ok(SearchResults { - total, - next_page, + details: SearchDetails { total, next_page }, items, }) } diff --git a/apps/backend/src/providers/audible.rs b/apps/backend/src/providers/audible.rs index c887f8f49a..c6c5ac5cef 100644 --- a/apps/backend/src/providers/audible.rs +++ b/apps/backend/src/providers/audible.rs @@ -14,7 +14,7 @@ use crate::{ AudioBookSpecifics, MediaDetails, MediaSearchItem, MediaSpecifics, MetadataCreator, MetadataImage, MetadataImageUrl, }, - NamedObject, SearchResults, + NamedObject, SearchDetails, SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, utils::{convert_date_to_year, convert_string_to_date, get_base_http_client, PAGE_LIMIT}, @@ -191,9 +191,11 @@ impl MediaProvider for AudibleService { None }; Ok(SearchResults { - total: search.total_results, + details: SearchDetails { + next_page, + total: search.total_results, + }, items: resp, - next_page, }) } } diff --git a/apps/backend/src/providers/google_books.rs b/apps/backend/src/providers/google_books.rs index d631460313..b018b72aeb 100644 --- a/apps/backend/src/providers/google_books.rs +++ b/apps/backend/src/providers/google_books.rs @@ -14,7 +14,7 @@ use crate::{ BookSpecifics, MediaDetails, MediaSearchItem, MediaSpecifics, MetadataCreator, MetadataImage, MetadataImageUrl, }, - SearchResults, + SearchDetails, SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, utils::{convert_date_to_year, get_base_http_client, PAGE_LIMIT}, @@ -147,9 +147,11 @@ impl MediaProvider for GoogleBooksService { None }; Ok(SearchResults { - total: search.total_items, + details: SearchDetails { + total: search.total_items, + next_page, + }, items: resp, - next_page, }) } } diff --git a/apps/backend/src/providers/igdb.rs b/apps/backend/src/providers/igdb.rs index c3706e6ee9..85cd78cdcc 100644 --- a/apps/backend/src/providers/igdb.rs +++ b/apps/backend/src/providers/igdb.rs @@ -14,7 +14,7 @@ use crate::{ MediaDetails, MediaSearchItem, MediaSpecifics, MetadataCreator, MetadataImage, MetadataImageUrl, VideoGameSpecifics, }, - NamedObject, SearchResults, + NamedObject, SearchDetails, SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, utils::PAGE_LIMIT, @@ -178,9 +178,11 @@ offset: {offset}; }) .collect_vec(); Ok(SearchResults { - total, + details: SearchDetails { + total, + next_page: Some(page + 1), + }, items: resp, - next_page: Some(page + 1), }) } } diff --git a/apps/backend/src/providers/itunes.rs b/apps/backend/src/providers/itunes.rs index 99455a9adf..77e0235ed0 100644 --- a/apps/backend/src/providers/itunes.rs +++ b/apps/backend/src/providers/itunes.rs @@ -15,7 +15,7 @@ use crate::{ MediaDetails, MediaSearchItem, MediaSpecifics, MetadataCreator, MetadataImage, MetadataImageUrl, PodcastEpisode, PodcastSpecifics, }, - NamedObject, SearchResults, + NamedObject, SearchDetails, SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, utils::{get_base_http_client, PAGE_LIMIT}, @@ -210,9 +210,11 @@ impl MediaProvider for ITunesService { let total = 100; Ok(SearchResults { - total, + details: SearchDetails { + total, + next_page: Some(page + 1), + }, items: resp, - next_page: Some(page + 1), }) } } diff --git a/apps/backend/src/providers/listennotes.rs b/apps/backend/src/providers/listennotes.rs index d9991a19be..5b1f5c63a4 100644 --- a/apps/backend/src/providers/listennotes.rs +++ b/apps/backend/src/providers/listennotes.rs @@ -18,7 +18,7 @@ use crate::{ MediaDetails, MediaSearchItem, MediaSpecifics, MetadataCreator, MetadataImage, MetadataImageUrl, PodcastEpisode, PodcastSpecifics, }, - SearchResults, + SearchDetails, SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, utils::PAGE_LIMIT, @@ -132,9 +132,8 @@ impl MediaProvider for ListennotesService { }) .collect_vec(); Ok(SearchResults { - total, + details: SearchDetails { total, next_page }, items: resp, - next_page, }) } } diff --git a/apps/backend/src/providers/music_brainz.rs b/apps/backend/src/providers/music_brainz.rs index 38572bd612..c63fd8787b 100644 --- a/apps/backend/src/providers/music_brainz.rs +++ b/apps/backend/src/providers/music_brainz.rs @@ -6,10 +6,9 @@ use surf::{http::headers::USER_AGENT, Client}; use crate::{ config::MusicBrainzConfig, - migrator::MetadataLot, models::{ media::{MediaDetails, MediaSearchItem}, - SearchResults, + SearchDetails, SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, utils::{convert_date_to_year, get_base_http_client, PAGE_LIMIT}, @@ -100,9 +99,11 @@ impl MediaProvider for MusicBrainzService { None }; Ok(SearchResults { - total: search.count, + details: SearchDetails { + total: search.count, + next_page, + }, items, - next_page, }) } } diff --git a/apps/backend/src/providers/openlibrary.rs b/apps/backend/src/providers/openlibrary.rs index c0f2a3514c..0c6aade920 100644 --- a/apps/backend/src/providers/openlibrary.rs +++ b/apps/backend/src/providers/openlibrary.rs @@ -23,7 +23,7 @@ use crate::{ BookSpecifics, MediaDetails, MediaSearchItem, MediaSpecifics, MetadataCreator, MetadataImage, MetadataImageUrl, }, - SearchResults, + SearchDetails, SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, utils::{get_base_http_client, PAGE_LIMIT}, @@ -355,8 +355,10 @@ impl MediaProvider for OpenlibraryService { None }; Ok(SearchResults { - total: data.total, - next_page, + details: SearchDetails { + total: data.total, + next_page, + }, items: data .items .into_iter() diff --git a/apps/backend/src/providers/tmdb.rs b/apps/backend/src/providers/tmdb.rs index 7cf6488eb2..a0c697fe6e 100644 --- a/apps/backend/src/providers/tmdb.rs +++ b/apps/backend/src/providers/tmdb.rs @@ -15,7 +15,7 @@ use crate::{ MediaDetails, MediaSearchItem, MediaSpecifics, MetadataCreator, MetadataImage, MetadataImageUrl, MovieSpecifics, ShowEpisode, ShowSeason, ShowSpecifics, }, - NamedObject, SearchResults, + NamedObject, SearchDetails, SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, utils::{convert_date_to_year, convert_string_to_date}, @@ -254,8 +254,10 @@ impl MediaProvider for TmdbMovieService { None }; Ok(SearchResults { - total: search.total_results, - next_page, + details: SearchDetails { + total: search.total_results, + next_page, + }, items: resp.to_vec(), }) } @@ -544,8 +546,10 @@ impl MediaProvider for TmdbShowService { None }; Ok(SearchResults { - total: search.total_results, - next_page, + details: SearchDetails { + total: search.total_results, + next_page, + }, items: resp.to_vec(), }) } diff --git a/libs/generated/src/graphql/backend/gql.ts b/libs/generated/src/graphql/backend/gql.ts index 7d84ee4aa7..edbd0d77fd 100644 --- a/libs/generated/src/graphql/backend/gql.ts +++ b/libs/generated/src/graphql/backend/gql.ts @@ -49,22 +49,22 @@ const documents = { "mutation UpdateUser($input: UpdateUserInput!) {\n updateUser(input: $input) {\n id\n }\n}": types.UpdateUserDocument, "mutation UpdateUserPreference($input: UpdateUserPreferenceInput!) {\n updateUserPreference(input: $input)\n}": types.UpdateUserPreferenceDocument, "mutation YankIntegrationData {\n yankIntegrationData\n}": types.YankIntegrationDataDocument, - "query CollectionContents($input: CollectionContentsInput!) {\n collectionContents(input: $input) {\n user {\n name\n }\n results {\n total\n nextPage\n items {\n lot\n details {\n identifier\n title\n image\n publishYear\n }\n }\n }\n details {\n name\n description\n visibility\n createdOn\n }\n }\n}": types.CollectionContentsDocument, + "query CollectionContents($input: CollectionContentsInput!) {\n collectionContents(input: $input) {\n user {\n name\n }\n results {\n details {\n total\n nextPage\n }\n items {\n lot\n details {\n identifier\n title\n image\n publishYear\n }\n }\n }\n details {\n name\n description\n visibility\n createdOn\n }\n }\n}": types.CollectionContentsDocument, "query Collections($input: CollectionInput) {\n collections(input: $input) {\n id\n name\n description\n visibility\n numItems\n }\n}": types.CollectionsDocument, "query CoreDetails {\n coreDetails {\n version\n authorName\n repositoryLink\n docsLink\n defaultCredentials\n passwordChangeAllowed\n preferencesChangeAllowed\n usernameChangeAllowed\n itemDetailsHeight\n reviewsDisabled\n upgrade\n }\n}": types.CoreDetailsDocument, "query CoreEnabledFeatures {\n coreEnabledFeatures {\n fileStorage\n signupAllowed\n }\n}": types.CoreEnabledFeaturesDocument, "query CreatorDetails($creatorId: Int!) {\n creatorDetails(creatorId: $creatorId) {\n details {\n id\n name\n image\n }\n contents {\n name\n items {\n lot\n details {\n identifier\n title\n image\n publishYear\n }\n }\n }\n }\n}": types.CreatorDetailsDocument, - "query CreatorsList($input: SearchInput!) {\n creatorsList(input: $input) {\n total\n nextPage\n items {\n id\n name\n image\n mediaCount\n }\n }\n}": types.CreatorsListDocument, + "query CreatorsList($input: SearchInput!) {\n creatorsList(input: $input) {\n details {\n total\n nextPage\n }\n items {\n id\n name\n image\n mediaCount\n }\n }\n}": types.CreatorsListDocument, "query Exercise($exerciseId: Int!) {\n exercise(exerciseId: $exerciseId) {\n name\n lot\n }\n}": types.ExerciseDocument, "query ExerciseInformation {\n exerciseInformation {\n filters {\n type\n level\n force\n mechanic\n equipment\n muscle\n }\n downloadRequired\n }\n}": types.ExerciseInformationDocument, - "query ExercisesList($input: ExercisesListInput!) {\n exercisesList(input: $input) {\n total\n nextPage\n items {\n id\n name\n lot\n equipment\n attributes {\n images\n }\n }\n }\n}": types.ExercisesListDocument, + "query ExercisesList($input: ExercisesListInput!) {\n exercisesList(input: $input) {\n details {\n total\n nextPage\n }\n items {\n id\n name\n lot\n equipment\n attributes {\n images\n }\n }\n }\n}": types.ExercisesListDocument, "query GetPresignedUrl($key: String!) {\n getPresignedUrl(key: $key)\n}": types.GetPresignedUrlDocument, "query ImportReports {\n importReports {\n id\n source\n startedOn\n finishedOn\n success\n details {\n import {\n total\n }\n failedItems {\n lot\n step\n identifier\n error\n }\n }\n }\n}": types.ImportReportsDocument, "query LatestUserSummary {\n latestUserSummary {\n calculatedOn\n fitness {\n measurementsRecorded\n }\n media {\n reviewsPosted\n creatorsInteractedWith\n manga {\n chapters\n read\n }\n books {\n pages\n read\n }\n movies {\n runtime\n watched\n }\n music {\n runtime\n listened\n }\n anime {\n episodes\n watched\n }\n podcasts {\n runtime\n played\n playedEpisodes\n }\n videoGames {\n played\n }\n shows {\n runtime\n watchedEpisodes\n watchedSeasons\n watched\n }\n audioBooks {\n runtime\n played\n }\n }\n }\n}": types.LatestUserSummaryDocument, "query MediaDetails($metadataId: Int!) {\n mediaDetails(metadataId: $metadataId) {\n ...MediaDetailsPart\n }\n}": types.MediaDetailsDocument, "fragment MediaDetailsPart on GraphqlMediaDetails {\n title\n description\n identifier\n lot\n source\n creators {\n name\n items {\n id\n name\n image\n }\n }\n posterImages\n backdropImages\n publishYear\n publishDate\n genres\n sourceUrl\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 title\n overview\n thumbnail\n number\n runtime\n }\n totalEpisodes\n }\n showSpecifics {\n seasons {\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 videoGameSpecifics {\n platforms\n }\n}": types.MediaDetailsPartFragmentDoc, - "query MediaList($input: MediaListInput!) {\n mediaList(input: $input) {\n total\n nextPage\n items {\n averageRating\n data {\n identifier\n title\n image\n publishYear\n }\n }\n }\n}": types.MediaListDocument, - "query MediaSearch($lot: MetadataLot!, $source: MetadataSource!, $input: SearchInput!) {\n mediaSearch(lot: $lot, source: $source, input: $input) {\n total\n nextPage\n items {\n databaseId\n item {\n identifier\n title\n image\n publishYear\n }\n }\n }\n}": types.MediaSearchDocument, + "query MediaList($input: MediaListInput!) {\n mediaList(input: $input) {\n details {\n total\n nextPage\n }\n items {\n averageRating\n data {\n identifier\n title\n image\n publishYear\n }\n }\n }\n}": types.MediaListDocument, + "query MediaSearch($lot: MetadataLot!, $source: MetadataSource!, $input: SearchInput!) {\n mediaSearch(lot: $lot, source: $source, input: $input) {\n details {\n total\n nextPage\n }\n items {\n databaseId\n item {\n identifier\n title\n image\n publishYear\n }\n }\n }\n}": types.MediaSearchDocument, "query MediaSourcesForLot($lot: MetadataLot!) {\n mediaSourcesForLot(lot: $lot)\n}": types.MediaSourcesForLotDocument, "query ProvidersLanguageInformation {\n providersLanguageInformation {\n supported\n default\n source\n }\n}": types.ProvidersLanguageInformationDocument, "query ReviewById($reviewId: Int!) {\n reviewById(reviewId: $reviewId) {\n rating\n text\n visibility\n spoiler\n showSeason\n showEpisode\n podcastEpisode\n }\n}": types.ReviewByIdDocument, @@ -240,7 +240,7 @@ export function graphql(source: "mutation YankIntegrationData {\n yankIntegrati /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query CollectionContents($input: CollectionContentsInput!) {\n collectionContents(input: $input) {\n user {\n name\n }\n results {\n total\n nextPage\n items {\n lot\n details {\n identifier\n title\n image\n publishYear\n }\n }\n }\n details {\n name\n description\n visibility\n createdOn\n }\n }\n}"): (typeof documents)["query CollectionContents($input: CollectionContentsInput!) {\n collectionContents(input: $input) {\n user {\n name\n }\n results {\n total\n nextPage\n items {\n lot\n details {\n identifier\n title\n image\n publishYear\n }\n }\n }\n details {\n name\n description\n visibility\n createdOn\n }\n }\n}"]; +export function graphql(source: "query CollectionContents($input: CollectionContentsInput!) {\n collectionContents(input: $input) {\n user {\n name\n }\n results {\n details {\n total\n nextPage\n }\n items {\n lot\n details {\n identifier\n title\n image\n publishYear\n }\n }\n }\n details {\n name\n description\n visibility\n createdOn\n }\n }\n}"): (typeof documents)["query CollectionContents($input: CollectionContentsInput!) {\n collectionContents(input: $input) {\n user {\n name\n }\n results {\n details {\n total\n nextPage\n }\n items {\n lot\n details {\n identifier\n title\n image\n publishYear\n }\n }\n }\n details {\n name\n description\n visibility\n createdOn\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -260,7 +260,7 @@ export function graphql(source: "query CreatorDetails($creatorId: Int!) {\n cre /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query CreatorsList($input: SearchInput!) {\n creatorsList(input: $input) {\n total\n nextPage\n items {\n id\n name\n image\n mediaCount\n }\n }\n}"): (typeof documents)["query CreatorsList($input: SearchInput!) {\n creatorsList(input: $input) {\n total\n nextPage\n items {\n id\n name\n image\n mediaCount\n }\n }\n}"]; +export function graphql(source: "query CreatorsList($input: SearchInput!) {\n creatorsList(input: $input) {\n details {\n total\n nextPage\n }\n items {\n id\n name\n image\n mediaCount\n }\n }\n}"): (typeof documents)["query CreatorsList($input: SearchInput!) {\n creatorsList(input: $input) {\n details {\n total\n nextPage\n }\n items {\n id\n name\n image\n mediaCount\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -272,7 +272,7 @@ export function graphql(source: "query ExerciseInformation {\n exerciseInformat /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query ExercisesList($input: ExercisesListInput!) {\n exercisesList(input: $input) {\n total\n nextPage\n items {\n id\n name\n lot\n equipment\n attributes {\n images\n }\n }\n }\n}"): (typeof documents)["query ExercisesList($input: ExercisesListInput!) {\n exercisesList(input: $input) {\n total\n nextPage\n items {\n id\n name\n lot\n equipment\n attributes {\n images\n }\n }\n }\n}"]; +export function graphql(source: "query ExercisesList($input: ExercisesListInput!) {\n exercisesList(input: $input) {\n details {\n total\n nextPage\n }\n items {\n id\n name\n lot\n equipment\n attributes {\n images\n }\n }\n }\n}"): (typeof documents)["query ExercisesList($input: ExercisesListInput!) {\n exercisesList(input: $input) {\n details {\n total\n nextPage\n }\n items {\n id\n name\n lot\n equipment\n attributes {\n images\n }\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -296,11 +296,11 @@ export function graphql(source: "fragment MediaDetailsPart on GraphqlMediaDetail /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query MediaList($input: MediaListInput!) {\n mediaList(input: $input) {\n total\n nextPage\n items {\n averageRating\n data {\n identifier\n title\n image\n publishYear\n }\n }\n }\n}"): (typeof documents)["query MediaList($input: MediaListInput!) {\n mediaList(input: $input) {\n total\n nextPage\n items {\n averageRating\n data {\n identifier\n title\n image\n publishYear\n }\n }\n }\n}"]; +export function graphql(source: "query MediaList($input: MediaListInput!) {\n mediaList(input: $input) {\n details {\n total\n nextPage\n }\n items {\n averageRating\n data {\n identifier\n title\n image\n publishYear\n }\n }\n }\n}"): (typeof documents)["query MediaList($input: MediaListInput!) {\n mediaList(input: $input) {\n details {\n total\n nextPage\n }\n items {\n averageRating\n data {\n identifier\n title\n image\n publishYear\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 MediaSearch($lot: MetadataLot!, $source: MetadataSource!, $input: SearchInput!) {\n mediaSearch(lot: $lot, source: $source, input: $input) {\n total\n nextPage\n items {\n databaseId\n item {\n identifier\n title\n image\n publishYear\n }\n }\n }\n}"): (typeof documents)["query MediaSearch($lot: MetadataLot!, $source: MetadataSource!, $input: SearchInput!) {\n mediaSearch(lot: $lot, source: $source, input: $input) {\n total\n nextPage\n items {\n databaseId\n item {\n identifier\n title\n image\n publishYear\n }\n }\n }\n}"]; +export function graphql(source: "query MediaSearch($lot: MetadataLot!, $source: MetadataSource!, $input: SearchInput!) {\n mediaSearch(lot: $lot, source: $source, input: $input) {\n details {\n total\n nextPage\n }\n items {\n databaseId\n item {\n identifier\n title\n image\n publishYear\n }\n }\n }\n}"): (typeof documents)["query MediaSearch($lot: MetadataLot!, $source: MetadataSource!, $input: SearchInput!) {\n mediaSearch(lot: $lot, source: $source, input: $input) {\n details {\n total\n nextPage\n }\n items {\n databaseId\n item {\n identifier\n title\n image\n publishYear\n }\n }\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 c7fc05041d..9dfc0eb76c 100644 --- a/libs/generated/src/graphql/backend/graphql.ts +++ b/libs/generated/src/graphql/backend/graphql.ts @@ -346,9 +346,8 @@ export enum ExerciseMuscle { } export type ExerciseSearchResults = { + details: SearchDetails; items: Array; - nextPage?: Maybe; - total: Scalars['Int']['output']; }; export type ExercisesListInput = { @@ -491,9 +490,8 @@ export type MangaSummary = { }; export type MediaCollectionContentsResults = { + details: SearchDetails; items: Array; - nextPage?: Maybe; - total: Scalars['Int']['output']; }; export type MediaCreatorSearchItem = { @@ -504,9 +502,8 @@ export type MediaCreatorSearchItem = { }; export type MediaCreatorSearchResults = { + details: SearchDetails; items: Array; - nextPage?: Maybe; - total: Scalars['Int']['output']; }; export type MediaFilter = { @@ -540,9 +537,8 @@ export type MediaListItem = { }; export type MediaListResults = { + details: SearchDetails; items: Array; - nextPage?: Maybe; - total: Scalars['Int']['output']; }; export type MediaSearchItem = { @@ -563,9 +559,8 @@ export type MediaSearchItemWithLot = { }; export type MediaSearchResults = { + details: SearchDetails; items: Array; - nextPage?: Maybe; - total: Scalars['Int']['output']; }; export enum MediaSortBy { @@ -1127,6 +1122,11 @@ export type ReviewPostedBy = { name: Scalars['String']['output']; }; +export type SearchDetails = { + nextPage?: Maybe; + total: Scalars['Int']['output']; +}; + export type SearchInput = { page?: InputMaybe; query?: InputMaybe; @@ -1782,7 +1782,7 @@ export type CollectionContentsQueryVariables = Exact<{ }>; -export type CollectionContentsQuery = { collectionContents: { user: { name: string }, results: { total: number, nextPage?: number | null, items: Array<{ lot: MetadataLot, details: { identifier: string, title: string, image?: string | null, publishYear?: number | null } }> }, details: { name: string, description?: string | null, visibility: Visibility, createdOn: Date } } }; +export type CollectionContentsQuery = { collectionContents: { user: { name: string }, results: { details: { total: number, nextPage?: number | null }, items: Array<{ lot: MetadataLot, details: { identifier: string, title: string, image?: string | null, publishYear?: number | null } }> }, details: { name: string, description?: string | null, visibility: Visibility, createdOn: Date } } }; export type CollectionsQueryVariables = Exact<{ input?: InputMaybe; @@ -1813,7 +1813,7 @@ export type CreatorsListQueryVariables = Exact<{ }>; -export type CreatorsListQuery = { creatorsList: { total: number, nextPage?: number | null, items: Array<{ id: number, name: string, image?: string | null, mediaCount: number }> } }; +export type CreatorsListQuery = { creatorsList: { details: { total: number, nextPage?: number | null }, items: Array<{ id: number, name: string, image?: string | null, mediaCount: number }> } }; export type ExerciseQueryVariables = Exact<{ exerciseId: Scalars['Int']['input']; @@ -1832,7 +1832,7 @@ export type ExercisesListQueryVariables = Exact<{ }>; -export type ExercisesListQuery = { exercisesList: { total: number, nextPage?: number | null, items: Array<{ id: number, name: string, lot: ExerciseLot, equipment?: ExerciseEquipment | null, attributes: { images: Array } }> } }; +export type ExercisesListQuery = { exercisesList: { details: { total: number, nextPage?: number | null }, items: Array<{ id: number, name: string, lot: ExerciseLot, equipment?: ExerciseEquipment | null, attributes: { images: Array } }> } }; export type GetPresignedUrlQueryVariables = Exact<{ key: Scalars['String']['input']; @@ -1865,7 +1865,7 @@ export type MediaListQueryVariables = Exact<{ }>; -export type MediaListQuery = { mediaList: { total: number, nextPage?: number | null, items: Array<{ averageRating?: any | null, data: { identifier: string, title: string, image?: string | null, publishYear?: number | null } }> } }; +export type MediaListQuery = { mediaList: { details: { total: number, nextPage?: number | null }, items: Array<{ averageRating?: any | null, data: { identifier: string, title: string, image?: string | null, publishYear?: number | null } }> } }; export type MediaSearchQueryVariables = Exact<{ lot: MetadataLot; @@ -1874,7 +1874,7 @@ export type MediaSearchQueryVariables = Exact<{ }>; -export type MediaSearchQuery = { mediaSearch: { total: number, nextPage?: number | null, items: Array<{ databaseId?: number | null, item: { identifier: string, title: string, image?: string | null, publishYear?: number | null } }> } }; +export type MediaSearchQuery = { mediaSearch: { details: { total: number, nextPage?: number | null }, items: Array<{ databaseId?: number | null, item: { identifier: string, title: string, image?: string | null, publishYear?: number | null } }> } }; export type MediaSourcesForLotQueryVariables = Exact<{ lot: MetadataLot; @@ -1986,21 +1986,21 @@ export const UpdateAllMetadataDocument = {"kind":"Document","definitions":[{"kin export const UpdateUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateUser"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateUserInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateUser"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; export const UpdateUserPreferenceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateUserPreference"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateUserPreferenceInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateUserPreference"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode; export const YankIntegrationDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"YankIntegrationData"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"yankIntegrationData"}}]}}]} as unknown as DocumentNode; -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":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"results"},"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":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}}]}}]}}]}},{"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":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}}]}}]}}]}}]} as unknown as DocumentNode; +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":"name"}}]}},{"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":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}}]}}]}}]}},{"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":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}}]}}]}}]}}]} as unknown as DocumentNode; export const CollectionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Collections"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CollectionInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"collections"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"numItems"}}]}}]}}]} as unknown as DocumentNode; 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":"authorName"}},{"kind":"Field","name":{"kind":"Name","value":"repositoryLink"}},{"kind":"Field","name":{"kind":"Name","value":"docsLink"}},{"kind":"Field","name":{"kind":"Name","value":"defaultCredentials"}},{"kind":"Field","name":{"kind":"Name","value":"passwordChangeAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"preferencesChangeAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"usernameChangeAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"itemDetailsHeight"}},{"kind":"Field","name":{"kind":"Name","value":"reviewsDisabled"}},{"kind":"Field","name":{"kind":"Name","value":"upgrade"}}]}}]}}]} as unknown as DocumentNode; export const CoreEnabledFeaturesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CoreEnabledFeatures"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"coreEnabledFeatures"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fileStorage"}},{"kind":"Field","name":{"kind":"Name","value":"signupAllowed"}}]}}]}}]} as unknown as DocumentNode; export const CreatorDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CreatorDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"creatorId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"creatorDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"creatorId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"creatorId"}}}],"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":"image"}}]}},{"kind":"Field","name":{"kind":"Name","value":"contents"},"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":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; -export const CreatorsListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CreatorsList"},"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":"creatorsList"},"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":"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":"image"}},{"kind":"Field","name":{"kind":"Name","value":"mediaCount"}}]}}]}}]}}]} as unknown as DocumentNode; +export const CreatorsListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CreatorsList"},"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":"creatorsList"},"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":"image"}},{"kind":"Field","name":{"kind":"Name","value":"mediaCount"}}]}}]}}]}}]} as unknown as DocumentNode; export const ExerciseDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Exercise"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"exerciseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"exercise"},"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":"name"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}}]}}]}}]} as unknown as DocumentNode; export const ExerciseInformationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ExerciseInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"exerciseInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"downloadRequired"}}]}}]}}]} as unknown as DocumentNode; -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":"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":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"equipment"}},{"kind":"Field","name":{"kind":"Name","value":"attributes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"images"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const ExercisesListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ExercisesList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ExercisesListInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"exercisesList"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}}]}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"equipment"}},{"kind":"Field","name":{"kind":"Name","value":"attributes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"images"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const GetPresignedUrlDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetPresignedUrl"},"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":"getPresignedUrl"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"key"},"value":{"kind":"Variable","name":{"kind":"Name","value":"key"}}}]}]}}]} as unknown as DocumentNode; 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":"startedOn"}},{"kind":"Field","name":{"kind":"Name","value":"finishedOn"}},{"kind":"Field","name":{"kind":"Name","value":"success"}},{"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":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const LatestUserSummaryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LatestUserSummary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"latestUserSummary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"calculatedOn"}},{"kind":"Field","name":{"kind":"Name","value":"fitness"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"measurementsRecorded"}}]}},{"kind":"Field","name":{"kind":"Name","value":"media"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reviewsPosted"}},{"kind":"Field","name":{"kind":"Name","value":"creatorsInteractedWith"}},{"kind":"Field","name":{"kind":"Name","value":"manga"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chapters"}},{"kind":"Field","name":{"kind":"Name","value":"read"}}]}},{"kind":"Field","name":{"kind":"Name","value":"books"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pages"}},{"kind":"Field","name":{"kind":"Name","value":"read"}}]}},{"kind":"Field","name":{"kind":"Name","value":"movies"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"watched"}}]}},{"kind":"Field","name":{"kind":"Name","value":"music"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"listened"}}]}},{"kind":"Field","name":{"kind":"Name","value":"anime"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"episodes"}},{"kind":"Field","name":{"kind":"Name","value":"watched"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podcasts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"played"}},{"kind":"Field","name":{"kind":"Name","value":"playedEpisodes"}}]}},{"kind":"Field","name":{"kind":"Name","value":"videoGames"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"played"}}]}},{"kind":"Field","name":{"kind":"Name","value":"shows"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"watchedEpisodes"}},{"kind":"Field","name":{"kind":"Name","value":"watchedSeasons"}},{"kind":"Field","name":{"kind":"Name","value":"watched"}}]}},{"kind":"Field","name":{"kind":"Name","value":"audioBooks"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"played"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const MediaDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MediaDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mediaDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"metadataId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"metadataId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"MediaDetailsPart"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MediaDetailsPart"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GraphqlMediaDetails"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"lot"}},{"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":"posterImages"}},{"kind":"Field","name":{"kind":"Name","value":"backdropImages"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}},{"kind":"Field","name":{"kind":"Name","value":"publishDate"}},{"kind":"Field","name":{"kind":"Name","value":"genres"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"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":"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":"totalEpisodes"}}]}},{"kind":"Field","name":{"kind":"Name","value":"showSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seasons"},"selectionSet":{"kind":"SelectionSet","selections":[{"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":"videoGameSpecifics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"platforms"}}]}}]}}]} as unknown as DocumentNode; -export const MediaListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MediaList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MediaListInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mediaList"},"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":"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":"averageRating"}},{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}}]}}]}}]}}]}}]} as unknown as DocumentNode; -export const MediaSearchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MediaSearch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"lot"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MetadataLot"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"source"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MetadataSource"}}}},{"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":"mediaSearch"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"lot"},"value":{"kind":"Variable","name":{"kind":"Name","value":"lot"}}},{"kind":"Argument","name":{"kind":"Name","value":"source"},"value":{"kind":"Variable","name":{"kind":"Name","value":"source"}}},{"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":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"databaseId"}},{"kind":"Field","name":{"kind":"Name","value":"item"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const MediaListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MediaList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MediaListInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mediaList"},"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":"averageRating"}},{"kind":"Field","name":{"kind":"Name","value":"data"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const MediaSearchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MediaSearch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"lot"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MetadataLot"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"source"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MetadataSource"}}}},{"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":"mediaSearch"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"lot"},"value":{"kind":"Variable","name":{"kind":"Name","value":"lot"}}},{"kind":"Argument","name":{"kind":"Name","value":"source"},"value":{"kind":"Variable","name":{"kind":"Name","value":"source"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"nextPage"}}]}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"databaseId"}},{"kind":"Field","name":{"kind":"Name","value":"item"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const MediaSourcesForLotDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MediaSourcesForLot"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"lot"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MetadataLot"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mediaSourcesForLot"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"lot"},"value":{"kind":"Variable","name":{"kind":"Name","value":"lot"}}}]}]}}]} as unknown as DocumentNode; export const ProvidersLanguageInformationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProvidersLanguageInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"providersLanguageInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"supported"}},{"kind":"Field","name":{"kind":"Name","value":"default"}},{"kind":"Field","name":{"kind":"Name","value":"source"}}]}}]}}]} as unknown as DocumentNode; export const ReviewByIdDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ReviewById"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"reviewId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reviewById"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"reviewId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"reviewId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"rating"}},{"kind":"Field","name":{"kind":"Name","value":"text"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"spoiler"}},{"kind":"Field","name":{"kind":"Name","value":"showSeason"}},{"kind":"Field","name":{"kind":"Name","value":"showEpisode"}},{"kind":"Field","name":{"kind":"Name","value":"podcastEpisode"}}]}}]}}]} as unknown as DocumentNode; diff --git a/libs/graphql/src/backend/queries/CollectionContents.gql b/libs/graphql/src/backend/queries/CollectionContents.gql index 0a23d2bb9a..d4b36342e9 100644 --- a/libs/graphql/src/backend/queries/CollectionContents.gql +++ b/libs/graphql/src/backend/queries/CollectionContents.gql @@ -4,8 +4,10 @@ query CollectionContents($input: CollectionContentsInput!) { name } results { - total - nextPage + details { + total + nextPage + } items { lot details { diff --git a/libs/graphql/src/backend/queries/CreatorsList.gql b/libs/graphql/src/backend/queries/CreatorsList.gql index 6cec7116b1..4c88e9ecf5 100644 --- a/libs/graphql/src/backend/queries/CreatorsList.gql +++ b/libs/graphql/src/backend/queries/CreatorsList.gql @@ -1,7 +1,9 @@ query CreatorsList($input: SearchInput!) { creatorsList(input: $input) { - total - nextPage + details { + total + nextPage + } items { id name diff --git a/libs/graphql/src/backend/queries/ExercisesList.gql b/libs/graphql/src/backend/queries/ExercisesList.gql index f5e55bf0f9..f1bfd2cb8f 100644 --- a/libs/graphql/src/backend/queries/ExercisesList.gql +++ b/libs/graphql/src/backend/queries/ExercisesList.gql @@ -1,7 +1,9 @@ query ExercisesList($input: ExercisesListInput!) { exercisesList(input: $input) { - total - nextPage + details { + total + nextPage + } items { id name diff --git a/libs/graphql/src/backend/queries/MediaList.gql b/libs/graphql/src/backend/queries/MediaList.gql index 3e2e947cca..64ee2a5164 100644 --- a/libs/graphql/src/backend/queries/MediaList.gql +++ b/libs/graphql/src/backend/queries/MediaList.gql @@ -1,7 +1,9 @@ query MediaList($input: MediaListInput!) { mediaList(input: $input) { - total - nextPage + details { + total + nextPage + } items { averageRating data { diff --git a/libs/graphql/src/backend/queries/MediaSearch.gql b/libs/graphql/src/backend/queries/MediaSearch.gql index 9ef8ad2eae..23853d894c 100644 --- a/libs/graphql/src/backend/queries/MediaSearch.gql +++ b/libs/graphql/src/backend/queries/MediaSearch.gql @@ -1,7 +1,9 @@ query MediaSearch($lot: MetadataLot!, $source: MetadataSource!, $input: SearchInput!) { mediaSearch(lot: $lot, source: $source, input: $input) { - total - nextPage + details { + total + nextPage + } items { databaseId item { From 93b5a17d9be2d9917f13eb540ccd7b80ae40cc34 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Thu, 17 Aug 2023 09:27:15 +0530 Subject: [PATCH 26/29] chore(frontend): adapt to new gql schema --- apps/frontend/src/pages/fitness/exercises/index.tsx | 8 ++++---- apps/frontend/src/pages/media/collections/index.tsx | 4 ++-- apps/frontend/src/pages/media/list.tsx | 12 ++++++------ apps/frontend/src/pages/media/people/list.tsx | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/frontend/src/pages/fitness/exercises/index.tsx b/apps/frontend/src/pages/fitness/exercises/index.tsx index 5f10f19457..d79b947422 100644 --- a/apps/frontend/src/pages/fitness/exercises/index.tsx +++ b/apps/frontend/src/pages/fitness/exercises/index.tsx @@ -231,11 +231,11 @@ const Page: NextPageWithLayout = () => { - {exercisesList.data && exercisesList.data.total > 0 ? ( + {exercisesList.data && exercisesList.data.details.total > 0 ? ( <> - {exercisesList.data.total} + {exercisesList.data.details.total} {" "} items found {selectionEnabled ? ( @@ -299,13 +299,13 @@ const Page: NextPageWithLayout = () => { ) : ( No information to display )} - {exercisesList.data && exercisesList.data.total > 0 ? ( + {exercisesList.data && exercisesList.data.details.total > 0 ? (
setPage(v.toString())} - total={Math.ceil(exercisesList.data.total / LIMIT)} + total={Math.ceil(exercisesList.data.details.total / LIMIT)} boundaries={1} siblings={0} /> diff --git a/apps/frontend/src/pages/media/collections/index.tsx b/apps/frontend/src/pages/media/collections/index.tsx index acc38ef140..076fd1cb33 100644 --- a/apps/frontend/src/pages/media/collections/index.tsx +++ b/apps/frontend/src/pages/media/collections/index.tsx @@ -57,7 +57,7 @@ const Page: NextPageWithLayout = () => { {collectionContents.data.details.name}{" "} - {collectionContents.data.results.total} items, created by{" "} + {collectionContents.data.results.details.total} items, created by{" "} {collectionContents.data.user.name}{" "} {formatTimeAgo(collectionContents.data.details.createdOn)} @@ -86,7 +86,7 @@ const Page: NextPageWithLayout = () => { size="sm" value={parseInt(activePage)} onChange={(v) => setPage(v.toString())} - total={Math.ceil(collectionContents.data.results.total / LIMIT)} + total={Math.ceil(collectionContents.data.results.details.total / LIMIT)} boundaries={1} siblings={0} /> diff --git a/apps/frontend/src/pages/media/list.tsx b/apps/frontend/src/pages/media/list.tsx index 934eb983ff..1df372bdda 100644 --- a/apps/frontend/src/pages/media/list.tsx +++ b/apps/frontend/src/pages/media/list.tsx @@ -379,11 +379,11 @@ const Page: NextPageWithLayout = () => { - {listMedia.data && listMedia.data.total > 0 ? ( + {listMedia.data && listMedia.data.details.total > 0 ? ( <> - {listMedia.data.total} + {listMedia.data.details.total} {" "} items found @@ -411,7 +411,7 @@ const Page: NextPageWithLayout = () => { size="sm" value={parseInt(activeMinePage)} onChange={(v) => setMinePage(v.toString())} - total={Math.ceil(listMedia.data.total / LIMIT)} + total={Math.ceil(listMedia.data.details.total / LIMIT)} boundaries={1} siblings={0} /> @@ -443,11 +443,11 @@ const Page: NextPageWithLayout = () => { /> ) : null} - {searchQuery.data && searchQuery.data.total > 0 ? ( + {searchQuery.data && searchQuery.data.details.total > 0 ? ( <> - {searchQuery.data.total} + {searchQuery.data.details.total} {" "} items found @@ -476,7 +476,7 @@ const Page: NextPageWithLayout = () => { size="sm" value={parseInt(activeSearchPage)} onChange={(v) => setSearchPage(v.toString())} - total={Math.ceil(searchQuery.data.total / LIMIT)} + total={Math.ceil(searchQuery.data.details.total / LIMIT)} boundaries={1} siblings={0} /> diff --git a/apps/frontend/src/pages/media/people/list.tsx b/apps/frontend/src/pages/media/people/list.tsx index 94d269e58a..2514a2b606 100644 --- a/apps/frontend/src/pages/media/people/list.tsx +++ b/apps/frontend/src/pages/media/people/list.tsx @@ -85,11 +85,11 @@ const Page: NextPageWithLayout = () => { /> - {listCreators.data && listCreators.data.total > 0 ? ( + {listCreators.data && listCreators.data.details.total > 0 ? ( <> - {listCreators.data.total} + {listCreators.data.details.total} {" "} items found @@ -117,7 +117,7 @@ const Page: NextPageWithLayout = () => { size="sm" value={parseInt(activePage)} onChange={(v) => setPage(v.toString())} - total={Math.ceil(listCreators.data.total / LIMIT)} + total={Math.ceil(listCreators.data.details.total / LIMIT)} boundaries={1} siblings={0} /> From f1a016d2203a7ece62a35e8b752ac80e02d21df3 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Thu, 17 Aug 2023 09:33:32 +0530 Subject: [PATCH 27/29] feat(*): get core page limit param from backend --- apps/backend/src/miscellaneous/resolver.rs | 3 +++ apps/frontend/src/lib/components/MediaItem.tsx | 1 - apps/frontend/src/lib/constants.ts | 2 -- .../src/pages/fitness/exercises/index.tsx | 11 ++++++++--- .../src/pages/media/collections/index.tsx | 11 ++++++++--- apps/frontend/src/pages/media/list.tsx | 17 +++++++++++------ apps/frontend/src/pages/media/people/list.tsx | 13 ++++++++++--- libs/generated/src/graphql/backend/gql.ts | 4 ++-- libs/generated/src/graphql/backend/graphql.ts | 6 ++++-- .../graphql/src/backend/queries/CoreDetails.gql | 1 + 10 files changed, 47 insertions(+), 22 deletions(-) diff --git a/apps/backend/src/miscellaneous/resolver.rs b/apps/backend/src/miscellaneous/resolver.rs index ae9b918a0b..f1641b9e64 100644 --- a/apps/backend/src/miscellaneous/resolver.rs +++ b/apps/backend/src/miscellaneous/resolver.rs @@ -474,6 +474,8 @@ struct CoreDetails { reviews_disabled: bool, /// Whether an upgrade is required upgrade: Option, + /// The number of elements on a page + page_limit: i32, } #[derive(Debug, Ord, PartialEq, Eq, PartialOrd, Clone)] @@ -1241,6 +1243,7 @@ impl MiscellaneousService { item_details_height: self.config.frontend.item_details_height, reviews_disabled: self.config.users.reviews_disabled, upgrade, + page_limit: PAGE_LIMIT, }) } diff --git a/apps/frontend/src/lib/components/MediaItem.tsx b/apps/frontend/src/lib/components/MediaItem.tsx index 7efdd25e74..0a7849cfcd 100644 --- a/apps/frontend/src/lib/components/MediaItem.tsx +++ b/apps/frontend/src/lib/components/MediaItem.tsx @@ -262,7 +262,6 @@ export default function (props: { item: Item; idx: number; query: string; - offset: number; lot: MetadataLot; source: MetadataSource; searchQueryRefetch: () => void; diff --git a/apps/frontend/src/lib/constants.ts b/apps/frontend/src/lib/constants.ts index f6c4397f6d..a104c700dd 100644 --- a/apps/frontend/src/lib/constants.ts +++ b/apps/frontend/src/lib/constants.ts @@ -1,5 +1,3 @@ -export const LIMIT = 20; - export const APP_ROUTES = { dashboard: "/", auth: { diff --git a/apps/frontend/src/pages/fitness/exercises/index.tsx b/apps/frontend/src/pages/fitness/exercises/index.tsx index d79b947422..189e884d6e 100644 --- a/apps/frontend/src/pages/fitness/exercises/index.tsx +++ b/apps/frontend/src/pages/fitness/exercises/index.tsx @@ -1,5 +1,6 @@ import type { NextPageWithLayout } from "../../_app"; -import { APP_ROUTES, LIMIT } from "@/lib/constants"; +import { APP_ROUTES } from "@/lib/constants"; +import { useCoreDetails } from "@/lib/hooks/graphql"; import LoadingPage from "@/lib/layouts/LoadingPage"; import LoggedIn from "@/lib/layouts/LoggedIn"; import { gqlClient } from "@/lib/services/api"; @@ -67,6 +68,7 @@ const defaultFilterValue = { const Page: NextPageWithLayout = () => { const router = useRouter(); const selectionEnabled = !!router.query.selectionEnabled; + const coreDetails = useCoreDetails(); const [selectedExercises, setSelectedExercises] = useListState<{ name: string; @@ -139,7 +141,7 @@ const Page: NextPageWithLayout = () => { ) : null; - return exerciseInformation.data ? ( + return coreDetails.data && exerciseInformation.data ? ( <> Exercises | Ryot @@ -305,7 +307,10 @@ const Page: NextPageWithLayout = () => { size="sm" value={parseInt(activePage)} onChange={(v) => setPage(v.toString())} - total={Math.ceil(exercisesList.data.details.total / LIMIT)} + total={Math.ceil( + exercisesList.data.details.total / + coreDetails.data.pageLimit, + )} boundaries={1} siblings={0} /> diff --git a/apps/frontend/src/pages/media/collections/index.tsx b/apps/frontend/src/pages/media/collections/index.tsx index 076fd1cb33..0ad14dc216 100644 --- a/apps/frontend/src/pages/media/collections/index.tsx +++ b/apps/frontend/src/pages/media/collections/index.tsx @@ -1,7 +1,8 @@ import type { NextPageWithLayout } from "../../_app"; import Grid from "@/lib/components/Grid"; import { MediaItemWithoutUpdateModal } from "@/lib/components/MediaItem"; -import { APP_ROUTES, LIMIT } from "@/lib/constants"; +import { APP_ROUTES } from "@/lib/constants"; +import { useCoreDetails } from "@/lib/hooks/graphql"; import LoadingPage from "@/lib/layouts/LoadingPage"; import LoggedIn from "@/lib/layouts/LoggedIn"; import { gqlClient } from "@/lib/services/api"; @@ -26,6 +27,7 @@ import { withQuery } from "ufo"; const Page: NextPageWithLayout = () => { const router = useRouter(); const collectionId = parseInt(router.query.collectionId?.toString() || "0"); + const coreDetails = useCoreDetails(); const [activePage, setPage] = useLocalStorage({ key: "savedPage", @@ -44,7 +46,7 @@ const Page: NextPageWithLayout = () => { { enabled: !!collectionId }, ); - return collectionId && collectionContents.data ? ( + return collectionId && coreDetails.data && collectionContents.data ? ( <> {collectionContents.data.details.name} | Ryot @@ -86,7 +88,10 @@ const Page: NextPageWithLayout = () => { size="sm" value={parseInt(activePage)} onChange={(v) => setPage(v.toString())} - total={Math.ceil(collectionContents.data.results.details.total / LIMIT)} + total={Math.ceil( + collectionContents.data.results.details.total / + coreDetails.data.pageLimit, + )} boundaries={1} siblings={0} /> diff --git a/apps/frontend/src/pages/media/list.tsx b/apps/frontend/src/pages/media/list.tsx index 1df372bdda..b3452a2d6c 100644 --- a/apps/frontend/src/pages/media/list.tsx +++ b/apps/frontend/src/pages/media/list.tsx @@ -3,7 +3,8 @@ import Grid from "@/lib/components/Grid"; import MediaItem, { MediaItemWithoutUpdateModal, } from "@/lib/components/MediaItem"; -import { APP_ROUTES, LIMIT } from "@/lib/constants"; +import { APP_ROUTES } from "@/lib/constants"; +import { useCoreDetails } from "@/lib/hooks/graphql"; import LoadingPage from "@/lib/layouts/LoadingPage"; import LoggedIn from "@/lib/layouts/LoggedIn"; import { gqlClient } from "@/lib/services/api"; @@ -123,7 +124,7 @@ const Page: NextPageWithLayout = () => { const router = useRouter(); const lot = getLot(router.query.lot); - const offset = (parseInt(activeSearchPage || "1") - 1) * LIMIT; + const coreDetails = useCoreDetails(); const listMedia = useQuery({ queryKey: [ @@ -248,7 +249,7 @@ const Page: NextPageWithLayout = () => { ); }; - return lot && collections.data ? ( + return lot && collections.data && coreDetails.data ? ( <> List {changeCase(lot).toLowerCase()}s | Ryot @@ -411,7 +412,9 @@ const Page: NextPageWithLayout = () => { size="sm" value={parseInt(activeMinePage)} onChange={(v) => setMinePage(v.toString())} - total={Math.ceil(listMedia.data.details.total / LIMIT)} + total={Math.ceil( + listMedia.data.details.total / coreDetails.data.pageLimit, + )} boundaries={1} siblings={0} /> @@ -459,7 +462,6 @@ const Page: NextPageWithLayout = () => { item={b.item} maybeItemId={b.databaseId ?? undefined} query={query} - offset={offset} lot={lot} searchQueryRefetch={searchQuery.refetch} source={searchSource as unknown as MetadataSource} @@ -476,7 +478,10 @@ const Page: NextPageWithLayout = () => { size="sm" value={parseInt(activeSearchPage)} onChange={(v) => setSearchPage(v.toString())} - total={Math.ceil(searchQuery.data.details.total / LIMIT)} + total={Math.ceil( + searchQuery.data.details.total / + coreDetails.data.pageLimit, + )} boundaries={1} siblings={0} /> diff --git a/apps/frontend/src/pages/media/people/list.tsx b/apps/frontend/src/pages/media/people/list.tsx index 2514a2b606..b6e734f0cd 100644 --- a/apps/frontend/src/pages/media/people/list.tsx +++ b/apps/frontend/src/pages/media/people/list.tsx @@ -1,7 +1,9 @@ import type { NextPageWithLayout } from "../../_app"; import Grid from "@/lib/components/Grid"; import { BaseDisplayItem } from "@/lib/components/MediaItem"; -import { APP_ROUTES, LIMIT } from "@/lib/constants"; +import { APP_ROUTES } from "@/lib/constants"; +import { useCoreDetails } from "@/lib/hooks/graphql"; +import LoadingPage from "@/lib/layouts/LoadingPage"; import LoggedIn from "@/lib/layouts/LoggedIn"; import { gqlClient } from "@/lib/services/api"; import { @@ -34,6 +36,7 @@ const Page: NextPageWithLayout = () => { getInitialValueInEffect: false, }); const [debouncedQuery, setDebouncedQuery] = useDebouncedState(query, 1000); + const coreDetails = useCoreDetails(); const listCreators = useQuery({ queryKey: ["creatorsList", activePage, debouncedQuery], @@ -63,7 +66,7 @@ const Page: NextPageWithLayout = () => { ) : null; - return ( + return coreDetails.data ? ( <> List People | Ryot @@ -117,7 +120,9 @@ const Page: NextPageWithLayout = () => { size="sm" value={parseInt(activePage)} onChange={(v) => setPage(v.toString())} - total={Math.ceil(listCreators.data.details.total / LIMIT)} + total={Math.ceil( + listCreators.data.details.total / coreDetails.data.pageLimit, + )} boundaries={1} siblings={0} /> @@ -126,6 +131,8 @@ const Page: NextPageWithLayout = () => { + ) : ( + ); }; diff --git a/libs/generated/src/graphql/backend/gql.ts b/libs/generated/src/graphql/backend/gql.ts index edbd0d77fd..a78a9b281e 100644 --- a/libs/generated/src/graphql/backend/gql.ts +++ b/libs/generated/src/graphql/backend/gql.ts @@ -51,7 +51,7 @@ const documents = { "mutation YankIntegrationData {\n yankIntegrationData\n}": types.YankIntegrationDataDocument, "query CollectionContents($input: CollectionContentsInput!) {\n collectionContents(input: $input) {\n user {\n name\n }\n results {\n details {\n total\n nextPage\n }\n items {\n lot\n details {\n identifier\n title\n image\n publishYear\n }\n }\n }\n details {\n name\n description\n visibility\n createdOn\n }\n }\n}": types.CollectionContentsDocument, "query Collections($input: CollectionInput) {\n collections(input: $input) {\n id\n name\n description\n visibility\n numItems\n }\n}": types.CollectionsDocument, - "query CoreDetails {\n coreDetails {\n version\n authorName\n repositoryLink\n docsLink\n defaultCredentials\n passwordChangeAllowed\n preferencesChangeAllowed\n usernameChangeAllowed\n itemDetailsHeight\n reviewsDisabled\n upgrade\n }\n}": types.CoreDetailsDocument, + "query CoreDetails {\n coreDetails {\n version\n authorName\n repositoryLink\n docsLink\n defaultCredentials\n passwordChangeAllowed\n preferencesChangeAllowed\n usernameChangeAllowed\n itemDetailsHeight\n reviewsDisabled\n upgrade\n pageLimit\n }\n}": types.CoreDetailsDocument, "query CoreEnabledFeatures {\n coreEnabledFeatures {\n fileStorage\n signupAllowed\n }\n}": types.CoreEnabledFeaturesDocument, "query CreatorDetails($creatorId: Int!) {\n creatorDetails(creatorId: $creatorId) {\n details {\n id\n name\n image\n }\n contents {\n name\n items {\n lot\n details {\n identifier\n title\n image\n publishYear\n }\n }\n }\n }\n}": types.CreatorDetailsDocument, "query CreatorsList($input: SearchInput!) {\n creatorsList(input: $input) {\n details {\n total\n nextPage\n }\n items {\n id\n name\n image\n mediaCount\n }\n }\n}": types.CreatorsListDocument, @@ -248,7 +248,7 @@ export function graphql(source: "query Collections($input: CollectionInput) {\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 CoreDetails {\n coreDetails {\n version\n authorName\n repositoryLink\n docsLink\n defaultCredentials\n passwordChangeAllowed\n preferencesChangeAllowed\n usernameChangeAllowed\n itemDetailsHeight\n reviewsDisabled\n upgrade\n }\n}"): (typeof documents)["query CoreDetails {\n coreDetails {\n version\n authorName\n repositoryLink\n docsLink\n defaultCredentials\n passwordChangeAllowed\n preferencesChangeAllowed\n usernameChangeAllowed\n itemDetailsHeight\n reviewsDisabled\n upgrade\n }\n}"]; +export function graphql(source: "query CoreDetails {\n coreDetails {\n version\n authorName\n repositoryLink\n docsLink\n defaultCredentials\n passwordChangeAllowed\n preferencesChangeAllowed\n usernameChangeAllowed\n itemDetailsHeight\n reviewsDisabled\n upgrade\n pageLimit\n }\n}"): (typeof documents)["query CoreDetails {\n coreDetails {\n version\n authorName\n repositoryLink\n docsLink\n defaultCredentials\n passwordChangeAllowed\n preferencesChangeAllowed\n usernameChangeAllowed\n itemDetailsHeight\n reviewsDisabled\n upgrade\n pageLimit\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 9dfc0eb76c..f593212153 100644 --- a/libs/generated/src/graphql/backend/graphql.ts +++ b/libs/generated/src/graphql/backend/graphql.ts @@ -116,6 +116,8 @@ export type CoreDetails = { defaultCredentials: Scalars['Boolean']['output']; docsLink: Scalars['String']['output']; itemDetailsHeight: Scalars['Int']['output']; + /** The number of elements on a page */ + pageLimit: Scalars['Int']['output']; passwordChangeAllowed: Scalars['Boolean']['output']; preferencesChangeAllowed: Scalars['Boolean']['output']; repositoryLink: Scalars['String']['output']; @@ -1794,7 +1796,7 @@ export type CollectionsQuery = { collections: Array<{ id: number, name: string, export type CoreDetailsQueryVariables = Exact<{ [key: string]: never; }>; -export type CoreDetailsQuery = { coreDetails: { version: string, authorName: string, repositoryLink: string, docsLink: string, defaultCredentials: boolean, passwordChangeAllowed: boolean, preferencesChangeAllowed: boolean, usernameChangeAllowed: boolean, itemDetailsHeight: number, reviewsDisabled: boolean, upgrade?: UpgradeType | null } }; +export type CoreDetailsQuery = { coreDetails: { version: string, authorName: string, repositoryLink: string, docsLink: string, defaultCredentials: boolean, passwordChangeAllowed: boolean, preferencesChangeAllowed: boolean, usernameChangeAllowed: boolean, itemDetailsHeight: number, reviewsDisabled: boolean, upgrade?: UpgradeType | null, pageLimit: number } }; export type CoreEnabledFeaturesQueryVariables = Exact<{ [key: string]: never; }>; @@ -1988,7 +1990,7 @@ export const UpdateUserPreferenceDocument = {"kind":"Document","definitions":[{" export const YankIntegrationDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"YankIntegrationData"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"yankIntegrationData"}}]}}]} as unknown as DocumentNode; 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":"name"}}]}},{"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":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}}]}}]}}]}},{"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":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"createdOn"}}]}}]}}]}}]} as unknown as DocumentNode; export const CollectionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Collections"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CollectionInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"collections"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"numItems"}}]}}]}}]} as unknown as DocumentNode; -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":"authorName"}},{"kind":"Field","name":{"kind":"Name","value":"repositoryLink"}},{"kind":"Field","name":{"kind":"Name","value":"docsLink"}},{"kind":"Field","name":{"kind":"Name","value":"defaultCredentials"}},{"kind":"Field","name":{"kind":"Name","value":"passwordChangeAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"preferencesChangeAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"usernameChangeAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"itemDetailsHeight"}},{"kind":"Field","name":{"kind":"Name","value":"reviewsDisabled"}},{"kind":"Field","name":{"kind":"Name","value":"upgrade"}}]}}]}}]} as unknown as DocumentNode; +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":"authorName"}},{"kind":"Field","name":{"kind":"Name","value":"repositoryLink"}},{"kind":"Field","name":{"kind":"Name","value":"docsLink"}},{"kind":"Field","name":{"kind":"Name","value":"defaultCredentials"}},{"kind":"Field","name":{"kind":"Name","value":"passwordChangeAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"preferencesChangeAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"usernameChangeAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"itemDetailsHeight"}},{"kind":"Field","name":{"kind":"Name","value":"reviewsDisabled"}},{"kind":"Field","name":{"kind":"Name","value":"upgrade"}},{"kind":"Field","name":{"kind":"Name","value":"pageLimit"}}]}}]}}]} as unknown as DocumentNode; export const CoreEnabledFeaturesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CoreEnabledFeatures"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"coreEnabledFeatures"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fileStorage"}},{"kind":"Field","name":{"kind":"Name","value":"signupAllowed"}}]}}]}}]} as unknown as DocumentNode; export const CreatorDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CreatorDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"creatorId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"creatorDetails"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"creatorId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"creatorId"}}}],"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":"image"}}]}},{"kind":"Field","name":{"kind":"Name","value":"contents"},"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":"lot"}},{"kind":"Field","name":{"kind":"Name","value":"details"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"identifier"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"publishYear"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const CreatorsListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CreatorsList"},"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":"creatorsList"},"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":"image"}},{"kind":"Field","name":{"kind":"Name","value":"mediaCount"}}]}}]}}]}}]} as unknown as DocumentNode; diff --git a/libs/graphql/src/backend/queries/CoreDetails.gql b/libs/graphql/src/backend/queries/CoreDetails.gql index 1e39d9397c..99cf5e008b 100644 --- a/libs/graphql/src/backend/queries/CoreDetails.gql +++ b/libs/graphql/src/backend/queries/CoreDetails.gql @@ -11,5 +11,6 @@ query CoreDetails { itemDetailsHeight reviewsDisabled upgrade + pageLimit } } From 620cb0a8b1fd2df9d262eface19b5a97a3a9c9e1 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Thu, 17 Aug 2023 21:34:33 +0530 Subject: [PATCH 28/29] feat(backend): config param for page limit --- apps/backend/src/config.rs | 3 +++ docs/includes/backend-config-schema.ts | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/apps/backend/src/config.rs b/apps/backend/src/config.rs index b6c78ed65f..84817904fb 100644 --- a/apps/backend/src/config.rs +++ b/apps/backend/src/config.rs @@ -321,6 +321,9 @@ pub struct FrontendConfig { /// The height of the right section of an item's details page in pixels. #[setting(default = 300)] pub item_details_height: u32, + /// The number of items to display in a list view. + #[setting(default = 20)] + pub page_size: i32, } #[derive(Debug, Serialize, Deserialize, Clone, Config)] diff --git a/docs/includes/backend-config-schema.ts b/docs/includes/backend-config-schema.ts index bc01011496..349795f3ae 100644 --- a/docs/includes/backend-config-schema.ts +++ b/docs/includes/backend-config-schema.ts @@ -84,6 +84,11 @@ export interface FrontendConfig { * @default 300 */ item_details_height: number; + /** + * The number of items to display in a list view. + * @default 20 + */ + page_size: number; } export interface IntegrationConfig { From 7c0742748598a2d7eb0bf768d68f707987f34a64 Mon Sep 17 00:00:00 2001 From: Diptesh Choudhuri Date: Thu, 17 Aug 2023 21:54:19 +0530 Subject: [PATCH 29/29] feat(backend): use config param for page size --- apps/backend/src/fitness/exercise/resolver.rs | 10 ++-- apps/backend/src/miscellaneous/resolver.rs | 50 ++++++++++++------- apps/backend/src/providers/anilist/mod.rs | 17 ++++--- apps/backend/src/providers/audible.rs | 11 ++-- apps/backend/src/providers/google_books.rs | 13 ++--- apps/backend/src/providers/igdb.rs | 9 ++-- apps/backend/src/providers/itunes.rs | 8 +-- apps/backend/src/providers/listennotes.rs | 12 +++-- apps/backend/src/providers/music_brainz.rs | 13 ++--- apps/backend/src/providers/openlibrary.rs | 16 +++--- apps/backend/src/providers/tmdb.rs | 7 ++- apps/backend/src/utils.rs | 2 +- 12 files changed, 101 insertions(+), 67 deletions(-) diff --git a/apps/backend/src/fitness/exercise/resolver.rs b/apps/backend/src/fitness/exercise/resolver.rs index fb4f379174..8fdc8a8949 100644 --- a/apps/backend/src/fitness/exercise/resolver.rs +++ b/apps/backend/src/fitness/exercise/resolver.rs @@ -13,6 +13,7 @@ use strum::IntoEnumIterator; use crate::{ background::UpdateExerciseJob, + config::AppConfig, entities::{ exercise, prelude::{Exercise, UserMeasurement}, @@ -30,7 +31,7 @@ use crate::{ SearchDetails, SearchResults, }, traits::AuthProvider, - utils::{get_case_insensitive_like_query, MemoryDatabase, PAGE_LIMIT}, + utils::{get_case_insensitive_like_query, MemoryDatabase}, }; static JSON_URL: &str = @@ -157,6 +158,7 @@ pub struct ExerciseService { db: DatabaseConnection, auth_db: MemoryDatabase, update_exercise: SqliteStorage, + config: Arc, } impl AuthProvider for ExerciseService { @@ -168,11 +170,13 @@ impl AuthProvider for ExerciseService { impl ExerciseService { pub fn new( db: &DatabaseConnection, + config: Arc, auth_db: MemoryDatabase, update_exercise: &SqliteStorage, ) -> Self { Self { db: db.clone(), + config, auth_db, update_exercise: update_exercise.clone(), } @@ -270,7 +274,7 @@ impl ExerciseService { .order_by_asc(exercise::Column::Name); let total = query.clone().count(&self.db).await?; let total: i32 = total.try_into().unwrap(); - let data = query.paginate(&self.db, PAGE_LIMIT.try_into().unwrap()); + let data = query.paginate(&self.db, self.config.frontend.page_size.try_into().unwrap()); let mut items = vec![]; for ex in data .fetch_page((input.page - 1).try_into().unwrap()) @@ -278,7 +282,7 @@ impl ExerciseService { { items.push(ex); } - let next_page = if total - ((input.page) * PAGE_LIMIT) > 0 { + let next_page = if total - ((input.page) * self.config.frontend.page_size) > 0 { Some(input.page + 1) } else { None diff --git a/apps/backend/src/miscellaneous/resolver.rs b/apps/backend/src/miscellaneous/resolver.rs index f1641b9e64..284d30f528 100644 --- a/apps/backend/src/miscellaneous/resolver.rs +++ b/apps/backend/src/miscellaneous/resolver.rs @@ -94,7 +94,7 @@ use crate::{ utils::{ associate_user_with_metadata, convert_naive_to_utc, get_case_insensitive_like_query, get_user_and_metadata_association, user_id_from_token, MemoryAuthData, MemoryDatabase, - AUTHOR, COOKIE_NAME, PAGE_LIMIT, USER_AGENT_STR, VERSION, + AUTHOR, COOKIE_NAME, USER_AGENT_STR, VERSION, }, }; @@ -1109,7 +1109,6 @@ impl MiscellaneousMutation { pub struct MiscellaneousService { pub db: DatabaseConnection, pub auth_db: MemoryDatabase, - pub config: Arc, pub files_storage_service: Arc, pub audible_service: AudibleService, pub google_books_service: GoogleBooksService, @@ -1127,6 +1126,7 @@ pub struct MiscellaneousService { pub recalculate_user_summary: SqliteStorage, pub user_created: SqliteStorage, seen_progress_cache: Arc>, + config: Arc, } impl AuthProvider for MiscellaneousService { @@ -1146,17 +1146,27 @@ impl MiscellaneousService { recalculate_user_summary: &SqliteStorage, user_created: &SqliteStorage, ) -> Self { - let openlibrary_service = OpenlibraryService::new(&config.books.openlibrary).await; - let google_books_service = GoogleBooksService::new(&config.books.google_books).await; - let tmdb_movies_service = TmdbMovieService::new(&config.movies.tmdb).await; - let tmdb_shows_service = TmdbShowService::new(&config.shows.tmdb).await; - let music_brainz_service = MusicBrainzService::new(&config.music.music_brainz).await; - let audible_service = AudibleService::new(&config.audio_books.audible).await; - let igdb_service = IgdbService::new(&config.video_games).await; - let itunes_service = ITunesService::new(&config.podcasts.itunes).await; - let listennotes_service = ListennotesService::new(&config.podcasts).await; - let anilist_anime_service = AnilistAnimeService::new(&config.anime.anilist).await; - let anilist_manga_service = AnilistMangaService::new(&config.manga.anilist).await; + let openlibrary_service = + OpenlibraryService::new(&config.books.openlibrary, config.frontend.page_size).await; + let google_books_service = + GoogleBooksService::new(&config.books.google_books, config.frontend.page_size).await; + let tmdb_movies_service = + TmdbMovieService::new(&config.movies.tmdb, config.frontend.page_size).await; + let tmdb_shows_service = + TmdbShowService::new(&config.shows.tmdb, config.frontend.page_size).await; + let music_brainz_service = + MusicBrainzService::new(&config.music.music_brainz, config.frontend.page_size).await; + let audible_service = + AudibleService::new(&config.audio_books.audible, config.frontend.page_size).await; + let igdb_service = IgdbService::new(&config.video_games, config.frontend.page_size).await; + let itunes_service = + ITunesService::new(&config.podcasts.itunes, config.frontend.page_size).await; + let listennotes_service = + ListennotesService::new(&config.podcasts, config.frontend.page_size).await; + let anilist_anime_service = + AnilistAnimeService::new(&config.anime.anilist, config.frontend.page_size).await; + let anilist_manga_service = + AnilistMangaService::new(&config.manga.anilist, config.frontend.page_size).await; let integration_service = IntegrationService::new().await; let seen_progress_cache = Arc::new(Cache::new()); @@ -1243,7 +1253,7 @@ impl MiscellaneousService { item_details_height: self.config.frontend.item_details_height, reviews_disabled: self.config.users.reviews_disabled, upgrade, - page_limit: PAGE_LIMIT, + page_limit: self.config.frontend.page_size, }) } @@ -1812,8 +1822,8 @@ impl MiscellaneousService { let total: i32 = total.try_into().unwrap(); let main_select = main_select - .limit(PAGE_LIMIT as u64) - .offset(((input.page - 1) * PAGE_LIMIT) as u64) + .limit(self.config.frontend.page_size as u64) + .offset(((input.page - 1) * self.config.frontend.page_size) as u64) .to_owned(); let stmt = self.get_db_stmt(main_select); let metas = InnerMediaSearchItem::find_by_statement(stmt) @@ -1865,7 +1875,7 @@ impl MiscellaneousService { }; items.push(m_small); } - let next_page = if total - ((input.page) * PAGE_LIMIT) > 0 { + let next_page = if total - ((input.page) * self.config.frontend.page_size) > 0 { Some(input.page + 1) } else { None @@ -2833,7 +2843,9 @@ impl MiscellaneousService { } let metas = collection.find_related(Metadata).paginate( &self.db, - input.take.unwrap_or_else(|| PAGE_LIMIT.try_into().unwrap()), + input + .take + .unwrap_or_else(|| self.config.frontend.page_size.try_into().unwrap()), ); let ItemsAndPagesNumber { @@ -4684,7 +4696,7 @@ impl MiscellaneousService { let creators_paginator = query .clone() .into_model::() - .paginate(&self.db, PAGE_LIMIT.try_into().unwrap()); + .paginate(&self.db, self.config.frontend.page_size.try_into().unwrap()); let ItemsAndPagesNumber { number_of_items, number_of_pages, diff --git a/apps/backend/src/providers/anilist/mod.rs b/apps/backend/src/providers/anilist/mod.rs index 8b236b859e..146c5d893e 100644 --- a/apps/backend/src/providers/anilist/mod.rs +++ b/apps/backend/src/providers/anilist/mod.rs @@ -12,7 +12,6 @@ use crate::{ SearchDetails, }, traits::{MediaProvider, MediaProviderLanguages}, - utils::PAGE_LIMIT, }; static URL: &str = "https://graphql.anilist.co"; @@ -36,6 +35,7 @@ struct DetailsQuery; #[derive(Debug, Clone)] pub struct AnilistService { client: Client, + page_limit: i32, } impl MediaProviderLanguages for AnilistService { @@ -54,10 +54,10 @@ pub struct AnilistAnimeService { } impl AnilistAnimeService { - pub async fn new(_config: &AnimeAnilistConfig) -> Self { + pub async fn new(_config: &AnimeAnilistConfig, page_limit: i32) -> Self { let client = utils::get_client_config(URL).await; Self { - base: AnilistService { client }, + base: AnilistService { client, page_limit }, } } } @@ -79,6 +79,7 @@ impl MediaProvider for AnilistAnimeService { search_query::MediaType::ANIME, query, page, + self.base.page_limit, ) .await?; Ok(SearchResults { @@ -94,10 +95,10 @@ pub struct AnilistMangaService { } impl AnilistMangaService { - pub async fn new(_config: &MangaAnilistConfig) -> Self { + pub async fn new(_config: &MangaAnilistConfig, page_limit: i32) -> Self { let client = utils::get_client_config(URL).await; Self { - base: AnilistService { client }, + base: AnilistService { client, page_limit }, } } } @@ -119,6 +120,7 @@ impl MediaProvider for AnilistMangaService { search_query::MediaType::MANGA, query, page, + self.base.page_limit, ) .await?; Ok(SearchResults { @@ -248,13 +250,14 @@ mod utils { media_type: search_query::MediaType, query: &str, page: Option, + page_limit: i32, ) -> Result<(Vec, i32, Option)> { let page = page.unwrap_or(1); let variables = search_query::Variables { page: page.into(), search: query.to_owned(), type_: media_type, - per_page: PAGE_LIMIT.into(), + per_page: page_limit.into(), }; let body = SearchQuery::build_query(variables); let search = client @@ -272,7 +275,7 @@ mod utils { .page .unwrap(); let total = search.page_info.unwrap().total.unwrap().try_into().unwrap(); - let next_page = if total - (page * PAGE_LIMIT) > 0 { + let next_page = if total - (page * page_limit) > 0 { Some(page + 1) } else { None diff --git a/apps/backend/src/providers/audible.rs b/apps/backend/src/providers/audible.rs index c6c5ac5cef..f83c2aa64c 100644 --- a/apps/backend/src/providers/audible.rs +++ b/apps/backend/src/providers/audible.rs @@ -17,7 +17,7 @@ use crate::{ NamedObject, SearchDetails, SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, - utils::{convert_date_to_year, convert_string_to_date, get_base_http_client, PAGE_LIMIT}, + utils::{convert_date_to_year, convert_string_to_date, get_base_http_client}, }; pub static LOCALES: [&str; 10] = ["au", "ca", "de", "es", "fr", "in", "it", "jp", "gb", "us"]; @@ -82,6 +82,7 @@ pub struct AudibleItem { #[derive(Debug, Clone)] pub struct AudibleService { client: Client, + page_limit: i32, } impl MediaProviderLanguages for AudibleService { @@ -112,10 +113,10 @@ impl AudibleService { format!("https://api.audible.{}/1.0/catalog/products/", suffix) } - pub async fn new(config: &AudibleConfig) -> Self { + pub async fn new(config: &AudibleConfig, page_limit: i32) -> Self { let url = Self::url_from_locale(&config.locale); let client = get_base_http_client(&url, vec![(ACCEPT, mime::JSON)]); - Self { client } + Self { client, page_limit } } } @@ -154,7 +155,7 @@ impl MediaProvider for AudibleService { .get("") .query(&SearchQuery { title: query.to_owned(), - num_results: PAGE_LIMIT, + num_results: self.page_limit, page: page - 1, products_sort_by: "Relevance".to_owned(), primary: PrimaryQuery::default(), @@ -185,7 +186,7 @@ impl MediaProvider for AudibleService { } }) .collect_vec(); - let next_page = if search.total_results - ((page) * PAGE_LIMIT) > 0 { + let next_page = if search.total_results - ((page) * self.page_limit) > 0 { Some(page + 1) } else { None diff --git a/apps/backend/src/providers/google_books.rs b/apps/backend/src/providers/google_books.rs index b018b72aeb..30e83050f2 100644 --- a/apps/backend/src/providers/google_books.rs +++ b/apps/backend/src/providers/google_books.rs @@ -17,7 +17,7 @@ use crate::{ SearchDetails, SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, - utils::{convert_date_to_year, get_base_http_client, PAGE_LIMIT}, + utils::{convert_date_to_year, get_base_http_client}, }; pub static URL: &str = "https://www.googleapis.com/books/v1/volumes/"; @@ -25,6 +25,7 @@ pub static URL: &str = "https://www.googleapis.com/books/v1/volumes/"; #[derive(Debug, Clone)] pub struct GoogleBooksService { client: Client, + page_limit: i32, } impl MediaProviderLanguages for GoogleBooksService { @@ -38,9 +39,9 @@ impl MediaProviderLanguages for GoogleBooksService { } impl GoogleBooksService { - pub async fn new(_config: &GoogleBooksConfig) -> Self { + pub async fn new(_config: &GoogleBooksConfig, page_limit: i32) -> Self { let client = get_base_http_client(URL, vec![(ACCEPT, mime::JSON)]); - Self { client } + Self { client, page_limit } } } @@ -98,13 +99,13 @@ impl MediaProvider for GoogleBooksService { page: Option, ) -> Result> { let page = page.unwrap_or(1); - let index = (page - 1) * PAGE_LIMIT; + let index = (page - 1) * self.page_limit; let mut rsp = self .client .get("") .query(&serde_json::json!({ "q": format!("intitle:{}", query), - "maxResults": PAGE_LIMIT, + "maxResults": self.page_limit, "printType": "books", "startIndex": index })) @@ -141,7 +142,7 @@ impl MediaProvider for GoogleBooksService { } }) .collect(); - let next_page = if search.total_items - ((page) * PAGE_LIMIT) > 0 { + let next_page = if search.total_items - ((page) * self.page_limit) > 0 { Some(page + 1) } else { None diff --git a/apps/backend/src/providers/igdb.rs b/apps/backend/src/providers/igdb.rs index 85cd78cdcc..97ccaa567e 100644 --- a/apps/backend/src/providers/igdb.rs +++ b/apps/backend/src/providers/igdb.rs @@ -17,7 +17,6 @@ use crate::{ NamedObject, SearchDetails, SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, - utils::PAGE_LIMIT, }; pub static URL: &str = "https://api.igdb.com/v4/"; @@ -81,6 +80,7 @@ pub struct IgdbService { image_url: String, image_size: String, config: VideoGameConfig, + page_limit: i32, } impl MediaProviderLanguages for IgdbService { @@ -94,11 +94,12 @@ impl MediaProviderLanguages for IgdbService { } impl IgdbService { - pub async fn new(config: &VideoGameConfig) -> Self { + pub async fn new(config: &VideoGameConfig, page_limit: i32) -> Self { Self { image_url: IMAGE_URL.to_owned(), image_size: config.igdb.image_size.to_string(), config: config.clone(), + page_limit, } } } @@ -142,8 +143,8 @@ limit {limit}; offset: {offset}; "#, field = FIELDS, - limit = PAGE_LIMIT, - offset = (page - 1) * PAGE_LIMIT + limit = self.page_limit, + offset = (page - 1) * self.page_limit ); let mut rsp = client .post("games") diff --git a/apps/backend/src/providers/itunes.rs b/apps/backend/src/providers/itunes.rs index 77e0235ed0..8e0f657d8c 100644 --- a/apps/backend/src/providers/itunes.rs +++ b/apps/backend/src/providers/itunes.rs @@ -18,7 +18,7 @@ use crate::{ NamedObject, SearchDetails, SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, - utils::{get_base_http_client, PAGE_LIMIT}, + utils::get_base_http_client, }; pub static URL: &str = "https://itunes.apple.com/"; @@ -27,6 +27,7 @@ pub static URL: &str = "https://itunes.apple.com/"; pub struct ITunesService { client: Client, language: String, + page_limit: i32, } impl MediaProviderLanguages for ITunesService { @@ -40,11 +41,12 @@ impl MediaProviderLanguages for ITunesService { } impl ITunesService { - pub async fn new(config: &ITunesConfig) -> Self { + pub async fn new(config: &ITunesConfig, page_limit: i32) -> Self { let client = get_base_http_client(URL, vec![(ACCEPT, mime::JSON)]); Self { client, language: config.locale.clone(), + page_limit, } } } @@ -190,7 +192,7 @@ impl MediaProvider for ITunesService { .get("search") .query(&serde_json::json!({ "term": query, - "limit": PAGE_LIMIT, + "limit": self.page_limit, "media": "podcast", "entity": "podcast", "lang": self.language diff --git a/apps/backend/src/providers/listennotes.rs b/apps/backend/src/providers/listennotes.rs index 5b1f5c63a4..9184e4653f 100644 --- a/apps/backend/src/providers/listennotes.rs +++ b/apps/backend/src/providers/listennotes.rs @@ -21,7 +21,6 @@ use crate::{ SearchDetails, SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, - utils::PAGE_LIMIT, }; pub static URL: &str = "https://listen-api.listennotes.com/api/v2/"; @@ -30,6 +29,7 @@ pub static URL: &str = "https://listen-api.listennotes.com/api/v2/"; pub struct ListennotesService { client: Client, genres: HashMap, + page_limit: i32, } impl MediaProviderLanguages for ListennotesService { @@ -43,9 +43,13 @@ impl MediaProviderLanguages for ListennotesService { } impl ListennotesService { - pub async fn new(config: &PodcastConfig) -> Self { + pub async fn new(config: &PodcastConfig, page_limit: i32) -> Self { let (client, genres) = utils::get_client_config(URL, &config.listennotes.api_token).await; - Self { client, genres } + Self { + client, + genres, + page_limit, + } } } @@ -110,7 +114,7 @@ impl MediaProvider for ListennotesService { .get("search") .query(&json!({ "q": query.to_owned(), - "offset": (page - 1) * PAGE_LIMIT, + "offset": (page - 1) * self.page_limit, "type": "podcast" })) .unwrap() diff --git a/apps/backend/src/providers/music_brainz.rs b/apps/backend/src/providers/music_brainz.rs index c63fd8787b..3f35118b30 100644 --- a/apps/backend/src/providers/music_brainz.rs +++ b/apps/backend/src/providers/music_brainz.rs @@ -11,7 +11,7 @@ use crate::{ SearchDetails, SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, - utils::{convert_date_to_year, get_base_http_client, PAGE_LIMIT}, + utils::{convert_date_to_year, get_base_http_client}, }; pub static URL: &str = "https://musicbrainz.org/ws/2/"; @@ -20,6 +20,7 @@ pub static IMAGES_URL: &str = "https://coverartarchive.org/"; #[derive(Debug, Clone)] pub struct MusicBrainzService { client: Client, + page_limit: i32, } impl MediaProviderLanguages for MusicBrainzService { @@ -33,13 +34,13 @@ impl MediaProviderLanguages for MusicBrainzService { } impl MusicBrainzService { - pub async fn new(config: &MusicBrainzConfig) -> Self { + pub async fn new(config: &MusicBrainzConfig, page_limit: i32) -> Self { let mut headers = vec![]; if let Some(ref u) = config.user_agent { headers.push((USER_AGENT, u.clone())); } let client = get_base_http_client(URL, headers); - Self { client } + Self { client, page_limit } } } @@ -75,8 +76,8 @@ impl MediaProvider for MusicBrainzService { .get("release-group") .query(&serde_json::json!({ "query": format!("release:{}", query), - "limit": PAGE_LIMIT, - "offset": (page - 1) * PAGE_LIMIT, + "limit": self.page_limit, + "offset": (page - 1) * self.page_limit, "fmt": "json", })) .unwrap() @@ -93,7 +94,7 @@ impl MediaProvider for MusicBrainzService { publish_year: r.first_release_date.and_then(|d| convert_date_to_year(&d)), }) .collect_vec(); - let next_page = if search.count - ((page) * PAGE_LIMIT) > 0 { + let next_page = if search.count - ((page) * self.page_limit) > 0 { Some(page + 1) } else { None diff --git a/apps/backend/src/providers/openlibrary.rs b/apps/backend/src/providers/openlibrary.rs index 0c6aade920..33c9b7d7f4 100644 --- a/apps/backend/src/providers/openlibrary.rs +++ b/apps/backend/src/providers/openlibrary.rs @@ -26,7 +26,7 @@ use crate::{ SearchDetails, SearchResults, }, traits::{MediaProvider, MediaProviderLanguages}, - utils::{get_base_http_client, PAGE_LIMIT}, + utils::get_base_http_client, }; static URL: &str = "https://openlibrary.org/"; @@ -67,13 +67,13 @@ impl Middleware for OpenlibraryRedirectMiddleware { } } -#[derive(Serialize, Deserialize, Debug, SimpleObject, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] struct BookSearchResults { total: i32, items: Vec, } -#[derive(Debug, Serialize, Deserialize, SimpleObject, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone)] struct BookSearchItem { identifier: String, title: String, @@ -96,6 +96,7 @@ pub struct OpenlibraryService { image_url: String, image_size: String, client: Client, + page_limit: i32, } impl MediaProviderLanguages for OpenlibraryService { @@ -109,13 +110,14 @@ impl MediaProviderLanguages for OpenlibraryService { } impl OpenlibraryService { - pub async fn new(config: &OpenlibraryConfig) -> Self { + pub async fn new(config: &OpenlibraryConfig, page_limit: i32) -> Self { let client = get_base_http_client(URL, vec![(ACCEPT, mime::JSON)]) .with(OpenlibraryRedirectMiddleware); Self { image_url: IMAGE_BASE_URL.to_owned(), image_size: config.cover_image_size.to_string(), client, + page_limit, } } } @@ -317,8 +319,8 @@ impl MediaProvider for OpenlibraryService { .query(&json!({ "q": query.to_owned(), "fields": fields, - "offset": (page - 1) * PAGE_LIMIT, - "limit": PAGE_LIMIT, + "offset": (page - 1) * self.page_limit, + "limit": self.page_limit, "type": "work".to_owned(), })) .unwrap() @@ -349,7 +351,7 @@ impl MediaProvider for OpenlibraryService { total: search.num_found, items: resp, }; - let next_page = if search.num_found - ((page) * PAGE_LIMIT) > 0 { + let next_page = if search.num_found - ((page) * self.page_limit) > 0 { Some(page + 1) } else { None diff --git a/apps/backend/src/providers/tmdb.rs b/apps/backend/src/providers/tmdb.rs index a0c697fe6e..002ec13af9 100644 --- a/apps/backend/src/providers/tmdb.rs +++ b/apps/backend/src/providers/tmdb.rs @@ -27,6 +27,7 @@ pub static URL: &str = "https://api.themoviedb.org/3/"; pub struct TmdbService { image_url: String, language: String, + page_limit: i32, } impl TmdbService { @@ -54,13 +55,14 @@ pub struct TmdbMovieService { } impl TmdbMovieService { - pub async fn new(config: &MoviesTmdbConfig) -> Self { + pub async fn new(config: &MoviesTmdbConfig, page_limit: i32) -> Self { let (client, image_url) = utils::get_client_config(URL, &config.access_token).await; Self { client, base: TmdbService { image_url, language: config.locale.clone(), + page_limit, }, } } @@ -270,13 +272,14 @@ pub struct TmdbShowService { } impl TmdbShowService { - pub async fn new(config: &ShowsTmdbConfig) -> Self { + pub async fn new(config: &ShowsTmdbConfig, page_limit: i32) -> Self { let (client, image_url) = utils::get_client_config(URL, &config.access_token).await; Self { client, base: TmdbService { image_url, language: config.locale.clone(), + page_limit, }, } } diff --git a/apps/backend/src/utils.rs b/apps/backend/src/utils.rs index a506472548..91de6def10 100644 --- a/apps/backend/src/utils.rs +++ b/apps/backend/src/utils.rs @@ -43,7 +43,6 @@ pub type MemoryDatabase = Arc>; pub static BASE_DIR: &str = env!("CARGO_MANIFEST_DIR"); pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const PROJECT_NAME: &str = env!("CARGO_PKG_NAME"); -pub const PAGE_LIMIT: i32 = 20; pub const COOKIE_NAME: &str = "auth"; pub const AUTHOR: &str = "ignisda"; pub const AUTHOR_EMAIL: &str = "ignisda2001@gmail.com"; @@ -88,6 +87,7 @@ pub async fn create_app_services( )); let exercise_service = Arc::new(ExerciseService::new( &db, + config.clone(), auth_db.clone(), update_exercise_job, ));