add favorite episode support to bookmarks

also delete unused bookmark file
This commit is contained in:
Pas 2025-09-29 17:47:50 -06:00
parent 371d9a27f7
commit ace0376ad5
5 changed files with 48 additions and 67 deletions

View file

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "bookmarks" ADD COLUMN "favorite_episodes" TEXT[] DEFAULT ARRAY[]::TEXT[];

View file

@ -9,11 +9,12 @@ datasource db {
} }
model bookmarks { model bookmarks {
tmdb_id String @db.VarChar(255) tmdb_id String @db.VarChar(255)
user_id String @db.VarChar(255) user_id String @db.VarChar(255)
meta Json meta Json
updated_at DateTime @db.Timestamptz(0) updated_at DateTime @db.Timestamptz(0)
group String[] group String[]
favorite_episodes String[] @default([])
@@id([tmdb_id, user_id]) @@id([tmdb_id, user_id])
@@unique([tmdb_id, user_id], map: "bookmarks_tmdb_id_user_id_unique") @@unique([tmdb_id, user_id], map: "bookmarks_tmdb_id_user_id_unique")

View file

@ -1,6 +1,15 @@
import { useAuth } from '~/utils/auth'; import { useAuth } from '~/utils/auth';
import { z } from 'zod'; import { z } from 'zod';
interface BookmarkWithFavorites {
tmdb_id: string;
user_id: string;
meta: any;
group: string[];
favorite_episodes: string[];
updated_at: Date;
}
const bookmarkMetaSchema = z.object({ const bookmarkMetaSchema = z.object({
title: z.string(), title: z.string(),
year: z.number().optional(), year: z.number().optional(),
@ -12,6 +21,7 @@ const bookmarkDataSchema = z.object({
tmdbId: z.string(), tmdbId: z.string(),
meta: bookmarkMetaSchema, meta: bookmarkMetaSchema,
group: z.union([z.string(), z.array(z.string())]).optional(), group: z.union([z.string(), z.array(z.string())]).optional(),
favoriteEpisodes: z.array(z.string()).optional(),
}); });
export default defineEventHandler(async event => { export default defineEventHandler(async event => {
@ -32,10 +42,11 @@ export default defineEventHandler(async event => {
where: { user_id: userId }, where: { user_id: userId },
}); });
return bookmarks.map(bookmark => ({ return bookmarks.map((bookmark: BookmarkWithFavorites) => ({
tmdbId: bookmark.tmdb_id, tmdbId: bookmark.tmdb_id,
meta: bookmark.meta, meta: bookmark.meta,
group: bookmark.group, group: bookmark.group,
favoriteEpisodes: bookmark.favorite_episodes,
updatedAt: bookmark.updated_at, updatedAt: bookmark.updated_at,
})); }));
} }
@ -53,6 +64,9 @@ export default defineEventHandler(async event => {
? (Array.isArray(item.group) ? item.group : [item.group]) ? (Array.isArray(item.group) ? item.group : [item.group])
: []; : [];
// Normalize favoriteEpisodes to always be an array
const normalizedFavoriteEpisodes = item.favoriteEpisodes || [];
const bookmark = await prisma.bookmarks.upsert({ const bookmark = await prisma.bookmarks.upsert({
where: { where: {
tmdb_id_user_id: { tmdb_id_user_id: {
@ -63,21 +77,24 @@ export default defineEventHandler(async event => {
update: { update: {
meta: item.meta, meta: item.meta,
group: normalizedGroup, group: normalizedGroup,
favorite_episodes: normalizedFavoriteEpisodes,
updated_at: now, updated_at: now,
}, } as any,
create: { create: {
tmdb_id: item.tmdbId, tmdb_id: item.tmdbId,
user_id: userId, user_id: userId,
meta: item.meta, meta: item.meta,
group: normalizedGroup, group: normalizedGroup,
favorite_episodes: normalizedFavoriteEpisodes,
updated_at: now, updated_at: now,
}, } as any,
}); }) as BookmarkWithFavorites;
results.push({ results.push({
tmdbId: bookmark.tmdb_id, tmdbId: bookmark.tmdb_id,
meta: bookmark.meta, meta: bookmark.meta,
group: bookmark.group, group: bookmark.group,
favoriteEpisodes: bookmark.favorite_episodes,
updatedAt: bookmark.updated_at, updatedAt: bookmark.updated_at,
}); });
} }
@ -85,60 +102,6 @@ export default defineEventHandler(async event => {
return results; return results;
} }
const segments = event.path.split('/');
const tmdbId = segments[segments.length - 1];
if (method === 'POST') {
const body = await readBody(event);
const validatedBody = bookmarkDataSchema.parse(body);
const existing = await prisma.bookmarks.findUnique({
where: {
tmdb_id_user_id: {
tmdb_id: tmdbId,
user_id: userId,
},
},
});
if (existing) {
throw createError({
statusCode: 400,
message: 'Already bookmarked',
});
}
const bookmark = await prisma.bookmarks.create({
data: {
tmdb_id: tmdbId,
user_id: userId,
meta: validatedBody.meta,
updated_at: new Date(),
},
});
return {
tmdbId: bookmark.tmdb_id,
meta: bookmark.meta,
group: bookmark.group,
updatedAt: bookmark.updated_at,
};
}
if (method === 'DELETE') {
try {
await prisma.bookmarks.delete({
where: {
tmdb_id_user_id: {
tmdb_id: tmdbId,
user_id: userId,
},
},
});
} catch (error) {}
return { tmdbId };
}
throw createError({ throw createError({
statusCode: 405, statusCode: 405,

View file

@ -2,6 +2,15 @@ import { useAuth } from '~/utils/auth';
import { z } from 'zod'; import { z } from 'zod';
import { scopedLogger } from '~/utils/logger'; import { scopedLogger } from '~/utils/logger';
interface BookmarkWithFavorites {
tmdb_id: string;
user_id: string;
meta: any;
group: string[];
favorite_episodes: string[];
updated_at: Date;
}
const log = scopedLogger('user-bookmarks'); const log = scopedLogger('user-bookmarks');
const bookmarkMetaSchema = z.object({ const bookmarkMetaSchema = z.object({
@ -16,6 +25,7 @@ const bookmarkRequestSchema = z.object({
meta: bookmarkMetaSchema.optional(), meta: bookmarkMetaSchema.optional(),
tmdbId: z.string().optional(), tmdbId: z.string().optional(),
group: z.union([z.string(), z.array(z.string())]).optional(), group: z.union([z.string(), z.array(z.string())]).optional(),
favoriteEpisodes: z.array(z.string()).optional(),
}); });
export default defineEventHandler(async event => { export default defineEventHandler(async event => {
@ -53,6 +63,9 @@ export default defineEventHandler(async event => {
? (Array.isArray(groupFromBody) ? groupFromBody : [groupFromBody]) ? (Array.isArray(groupFromBody) ? groupFromBody : [groupFromBody])
: []; : [];
// Normalize favoriteEpisodes to always be an array
const normalizedFavoriteEpisodes = validatedRequest.favoriteEpisodes || [];
const bookmark = await prisma.bookmarks.upsert({ const bookmark = await prisma.bookmarks.upsert({
where: { where: {
tmdb_id_user_id: { tmdb_id_user_id: {
@ -63,16 +76,18 @@ export default defineEventHandler(async event => {
update: { update: {
meta: validatedMeta, meta: validatedMeta,
group: normalizedGroup, group: normalizedGroup,
favorite_episodes: normalizedFavoriteEpisodes,
updated_at: new Date(), updated_at: new Date(),
}, } as any,
create: { create: {
user_id: session.user, user_id: session.user,
tmdb_id: tmdbId, tmdb_id: tmdbId,
meta: validatedMeta, meta: validatedMeta,
group: normalizedGroup, group: normalizedGroup,
favorite_episodes: normalizedFavoriteEpisodes,
updated_at: new Date(), updated_at: new Date(),
}, } as any,
}); }) as BookmarkWithFavorites;
log.info('Bookmark created successfully', { userId, tmdbId }); log.info('Bookmark created successfully', { userId, tmdbId });
@ -80,6 +95,7 @@ export default defineEventHandler(async event => {
tmdbId: bookmark.tmdb_id, tmdbId: bookmark.tmdb_id,
meta: bookmark.meta, meta: bookmark.meta,
group: bookmark.group, group: bookmark.group,
favoriteEpisodes: bookmark.favorite_episodes,
updatedAt: bookmark.updated_at, updatedAt: bookmark.updated_at,
}; };
} catch (error) { } catch (error) {

View file

@ -1 +0,0 @@
import { prisma } from '~/utils/prisma';