Accept movie progress from all clients and return resume position on movie detail.

This commit is contained in:
Jos Vooges | STH 2026-09-28 02:02:04 +02:00
parent e6607654a2
commit b783ba137f
2 changed files with 23 additions and 5 deletions

View file

@ -642,9 +642,9 @@ export function registerViewerRoutes(
app.get("/api/v1/client/movies/:id", async (request) => {
const auth = await viewers.authFromBearer(request.headers.authorization);
requireProfile(auth);
const profileId = requireProfile(auth);
const { id } = request.params as { id: string };
return viewers.movieDetail(id, auth.profile?.isKids === true);
return viewers.movieDetail(id, auth.profile?.isKids === true, profileId);
});
app.get("/api/v1/client/series/:id", async (request) => {
@ -684,8 +684,12 @@ export function registerViewerRoutes(
hiddenFromContinue?: boolean;
};
if (!body.mediaType) throw new AppError("INVALID_REQUEST", "mediaType verplicht", 400);
const mediaType = String(body.mediaType).trim().toUpperCase();
if (mediaType !== "MOVIE" && mediaType !== "EPISODE") {
throw new AppError("INVALID_REQUEST", "mediaType moet MOVIE of EPISODE zijn", 400);
}
const row = await viewers.upsertProgress(profileId, {
mediaType: body.mediaType,
mediaType: mediaType as "MOVIE" | "EPISODE",
movieId: body.movieId,
episodeId: body.episodeId,
seriesId: body.seriesId,

View file

@ -2026,13 +2026,17 @@ export class ViewerService {
hiddenFromContinue?: boolean;
}
) {
const mediaType = String(body.mediaType || "").trim().toUpperCase();
if (mediaType !== "MOVIE" && mediaType !== "EPISODE") {
throw new AppError("INVALID_REQUEST", "mediaType moet MOVIE of EPISODE zijn", 400);
}
const positionMs = Math.max(0, Math.floor(body.positionMs || 0));
const durationMs = Math.max(0, Math.floor(body.durationMs || 0));
const autoComplete =
body.completed === true ||
(durationMs > 0 && positionMs / durationMs >= 0.9);
if (body.mediaType === "MOVIE") {
if (mediaType === "MOVIE") {
if (!body.movieId) throw new AppError("INVALID_REQUEST", "movieId verplicht", 400);
const existingMovie = await prisma.watchProgress.findUnique({
where: { profileId_movieId: { profileId, movieId: body.movieId } },
@ -2523,7 +2527,7 @@ export class ViewerService {
return this.getShelfPrefs(profileId);
}
async movieDetail(movieId: string, kidsMode = false) {
async movieDetail(movieId: string, kidsMode = false, profileId?: string | null) {
const movie = await prisma.movie.findUnique({
where: { id: movieId },
include: {
@ -2552,6 +2556,12 @@ export class ViewerService {
.catch(() => undefined);
}
const progress = profileId
? await prisma.watchProgress.findUnique({
where: { profileId_movieId: { profileId, movieId: movie.id } },
})
: null;
return {
type: "movie" as const,
id: movie.id,
@ -2567,6 +2577,10 @@ export class ViewerService {
cast: extras.cast ?? [],
director: extras.director ?? [],
trailerYoutubeKey: extras.trailers?.[0]?.source ?? null,
positionMs: progress && !progress.completed ? progress.positionMs : 0,
durationMs: progress?.durationMs ?? 0,
completed: progress?.completed === true,
progressId: progress?.id ?? null,
mediaFiles: movie.mediaFiles.map((f, index) => ({
id: f.id,
resolution: cleanMeta(f.resolution),