Skip to content

Commit

Permalink
Bidirectional Jellyfin sync (#1075)
Browse files Browse the repository at this point in the history
* chore(doc): change attributes

* chore(backend): change name to jellyfin sink

* refactor(services/integration): remove one layer of indirection

* chore(gql): regenerate types

* feat(backend): add new integration provider

* chore(backend): change names of fields

* feat(frontend): allow saving jellyfin push integration

* chore(frontend): remove useless schema

* chore(backend): add new event to handle media seen events

* chore(backend): fn to handle deploying media seen event

* refactor(backend): functions to deploy jobs

* refactor(backend): break services a bit more

* fix(services/importer): include token correctly

* chore(services/integrations): scaffold stuff for new jellyfin push integration

* chore(backend): add new crate for external stuff

* refactor(backend): extract authentication stuff to new utils crate

* feat(services/integrations): start with pushing progress to jellyfin

* docs: add info about new push integration

* fix(frontend): do not allow creating/disabling account access links

If the currently active user is under a demo link.

* fix(backend): use correct user agent str for headers

* feat(frontend): make creating workout/template client side

* fix(frontend): handle supersets when deleting exercise from workout

* feat(frontend): change btn for editing superset

* refactor(services/integrations): change names of structs

* chore(backend): log the request

* feat(services/integrations): send request to get movie

* feat(services/integrations): allow searching for movie

* feat(services/integrations): allow marking movie as played

* chore(services/integrations): send info about which show was played

* feat(services/integrations): allow marking shows as seen

* feat(migrations): add new column for video games duration

* chore(backend): add manual spent time to video game duration

* feat(frontend): display total video game duration

* fix(backend): add video game time to total duration

* fix(migrations): add column if it does not exist

* fix(services/statistics): use correct manual time spent

* fix(services/statistics): use correct manual time spent

* feat(frontend): make jellyfin push a pro feature

* chore(ts): update all dependencies other than remix

* chore(ts): upgrade conform deps

* chore(website): upgrade to latest remix

* chore(frontend): upgrade to latest remix

* ci: Run CI

* chore(frontend): use new utility function

* refactor(frontend): remove usage of `namedAction`

* refactor(frontend): move utility function to utils package

* refactor(website): use new utility function
  • Loading branch information
IgnisDa authored Oct 23, 2024
1 parent e7ab8eb commit 177b797
Show file tree
Hide file tree
Showing 133 changed files with 3,682 additions and 3,716 deletions.
23 changes: 17 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ members = [
"crates/utils/common",
"crates/utils/database",
"crates/utils/dependent",
"crates/utils/external",
"crates/utils/env",
]
resolver = "2"
Expand Down
8 changes: 8 additions & 0 deletions apps/backend/src/job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ pub async fn perform_application_job(
.update_person_and_notify_users(person_id)
.await
.is_ok(),
ApplicationJob::HandleAfterMediaSeenTasks(seen) => misc_service
.handle_after_media_seen_tasks(seen)
.await
.is_ok(),
ApplicationJob::UpdateMetadataGroup(metadata_group_id) => misc_service
.update_metadata_group(&metadata_group_id)
.await
Expand Down Expand Up @@ -135,6 +139,10 @@ pub async fn perform_application_job(
.await
.is_ok()
}
ApplicationJob::HandleOnSeenComplete(id) => integration_service
.handle_on_seen_complete(id)
.await
.is_ok(),
};
ryot_log!(
trace,
Expand Down
15 changes: 9 additions & 6 deletions apps/frontend/app/lib/utilities.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
} from "@ryot/generated/graphql/backend/graphql";
import { UserDetailsDocument } from "@ryot/generated/graphql/backend/graphql";
import { isEmpty } from "@ryot/ts-utils";
import { type CookieSerializeOptions, parse, serialize } from "cookie";
import { type SerializeOptions, parse, serialize } from "cookie";
import {
ClientError,
GraphQLClient,
Expand Down Expand Up @@ -176,7 +176,7 @@ export const getCachedCoreDetails = async () => {
export const getCachedUserDetails = async (request: Request) => {
const token = getAuthorizationCookie(request);
return await queryClient.ensureQueryData({
queryKey: queryFactory.users.details(token).queryKey,
queryKey: queryFactory.users.details(token ?? "").queryKey,
queryFn: () =>
serverGqlService.authenticatedRequest(
request,
Expand Down Expand Up @@ -366,7 +366,7 @@ export const getCookiesForApplication = async (
const [{ coreDetails }] = await Promise.all([getCachedCoreDetails()]);
const maxAge =
(tokenValidForDays || coreDetails.tokenValidForDays) * 24 * 60 * 60;
const options = { maxAge, path: "/" } satisfies CookieSerializeOptions;
const options = { maxAge, path: "/" } satisfies SerializeOptions;
return combineHeaders({
"set-cookie": serialize(AUTH_COOKIE_NAME, token, options),
});
Expand All @@ -391,9 +391,12 @@ export const getWorkoutCookieValue = (request: Request) => {
};

export const isWorkoutActive = (request: Request) => {
const inProgress = [FitnessAction.LogWorkout, FitnessAction.UpdateWorkout]
.map(String)
.includes(getWorkoutCookieValue(request));
const cookieValue = getWorkoutCookieValue(request);
const inProgress =
cookieValue &&
[FitnessAction.LogWorkout, FitnessAction.UpdateWorkout]
.map(String)
.includes(cookieValue);
return inProgress;
};

Expand Down
10 changes: 5 additions & 5 deletions apps/frontend/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import "@mantine/core/styles.css";
import "@mantine/notifications/styles.css";
import {
type LinksFunction,
type LoaderFunctionArgs,
type MetaFunction,
unstable_data,
unstable_defineLoader,
data,
} from "@remix-run/node";
import {
Links,
Expand Down Expand Up @@ -93,7 +93,7 @@ export const links: LinksFunction = () => {
];
};

export const loader = unstable_defineLoader(async ({ request }) => {
export const loader = async ({ request }: LoaderFunctionArgs) => {
const { toast, headers: toastHeaders } = await getToast(request);
const colorScheme = await colorSchemeCookie.parse(
request.headers.get("cookie") || "",
Expand All @@ -114,8 +114,8 @@ export const loader = unstable_defineLoader(async ({ request }) => {
}
}

return unstable_data({ toast, defaultColorScheme, isIOS18 }, { headers });
});
return data({ toast, defaultColorScheme, isIOS18 }, { headers });
};

const DefaultHeadTags = () => {
const loaderData = useLoaderData<typeof loader>();
Expand Down
15 changes: 10 additions & 5 deletions apps/frontend/app/routes/_dashboard._index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ import {
useMantineTheme,
} from "@mantine/core";
import { useInViewport } from "@mantine/hooks";
import { unstable_defineLoader } from "@remix-run/node";
import type { MetaArgs_SingleFetch } from "@remix-run/react";
import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
import { Link, useLoaderData } from "@remix-run/react";
import {
type CalendarEventPartFragment,
Expand Down Expand Up @@ -89,7 +88,7 @@ const getTake = (preferences: UserPreferences, el: DashboardElementLot) => {
return t;
};

export const loader = unstable_defineLoader(async ({ request }) => {
export const loader = async ({ request }: LoaderFunctionArgs) => {
const preferences = await getCachedUserPreferences(request);
const takeUpcoming = getTake(preferences, DashboardElementLot.Upcoming);
const takeInProgress = getTake(preferences, DashboardElementLot.InProgress);
Expand Down Expand Up @@ -143,9 +142,9 @@ export const loader = unstable_defineLoader(async ({ request }) => {
inProgressCollectionContents,
userRecommendations,
};
});
};

export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => {
export const meta = (_args: MetaArgs<typeof loader>) => {
return [{ title: "Home | Ryot" }];
};

Expand Down Expand Up @@ -282,6 +281,12 @@ export default function Page() {
value: latestUserSummary.videoGameCount,
type: "number",
},
{
label: "Runtime",
value: latestUserSummary.totalVideoGameDuration,
type: "duration",
hideIfZero: true,
},
]}
/>
<DisplayStatForMediaType
Expand Down
14 changes: 5 additions & 9 deletions apps/frontend/app/routes/_dashboard.calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,8 @@ import {
Title,
Tooltip,
} from "@mantine/core";
import { unstable_defineLoader } from "@remix-run/node";
import {
Link,
type MetaArgs_SingleFetch,
useLoaderData,
} from "@remix-run/react";
import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
import { Link, useLoaderData } from "@remix-run/react";
import {
UserCalendarEventsDocument,
type UserCalendarEventsQuery,
Expand All @@ -40,7 +36,7 @@ const searchParamsSchema = z.object({

export type SearchParams = z.infer<typeof searchParamsSchema>;

export const loader = unstable_defineLoader(async ({ request }) => {
export const loader = async ({ request }: LoaderFunctionArgs) => {
const cookieName = await getEnhancedCookieName("calendar", request);
await redirectUsingEnhancedCookieSearchParams(request, cookieName);
const query = zx.parseQuery(request, searchParamsSchema);
Expand All @@ -51,9 +47,9 @@ export const loader = unstable_defineLoader(async ({ request }) => {
}),
]);
return { query, userCalendarEvents, cookieName };
});
};

export const meta = (_args: MetaArgs_SingleFetch<typeof loader>) => {
export const meta = (_args: MetaArgs<typeof loader>) => {
return [{ title: "Calendar | Ryot" }];
};

Expand Down
14 changes: 5 additions & 9 deletions apps/frontend/app/routes/_dashboard.collections.$id._index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,8 @@ import {
Title,
} from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { unstable_defineLoader } from "@remix-run/node";
import {
type MetaArgs_SingleFetch,
useLoaderData,
useNavigate,
} from "@remix-run/react";
import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
import { useLoaderData, useNavigate } from "@remix-run/react";
import {
CollectionContentsDocument,
CollectionContentsSortBy,
Expand Down Expand Up @@ -81,7 +77,7 @@ const searchParamsSchema = z.object({

export type SearchParams = z.infer<typeof searchParamsSchema>;

export const loader = unstable_defineLoader(async ({ request, params }) => {
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
const { id: collectionId } = zx.parseParams(params, { id: z.string() });
const cookieName = await getEnhancedCookieName(
`collections.details.${collectionId}`,
Expand All @@ -108,9 +104,9 @@ export const loader = unstable_defineLoader(async ({ request, params }) => {
query[pageQueryParam] || 1,
);
return { collectionId, query, collectionContents, cookieName, totalPages };
});
};

export const meta = ({ data }: MetaArgs_SingleFetch<typeof loader>) => {
export const meta = ({ data }: MetaArgs<typeof loader>) => {
return [{ title: `${data?.collectionContents.details.name} | Ryot` }];
};

Expand Down
Loading

0 comments on commit 177b797

Please sign in to comment.