v1 feature: added playlists

This commit is contained in:
khannurien
2026-03-16 16:52:53 +00:00
parent 867e64cb5b
commit be426eb150
25 changed files with 2958 additions and 101 deletions

View File

@@ -1,5 +1,10 @@
import { DatabaseSync, type SQLOutputValue } from "node:sqlite";
import { Dump, type RichContent, type User } from "./interfaces.ts";
import {
Dump,
type Playlist,
type RichContent,
type User,
} from "./interfaces.ts";
export const db = new DatabaseSync("api/sql/gerbeur.db");
db.exec("PRAGMA foreign_keys = ON;");
@@ -133,3 +138,37 @@ export function userApiToRow(user: User): UserRow {
avatar_mime: user.avatarMime ?? null,
};
}
export interface PlaylistRow {
id: string;
user_id: string;
title: string;
description: string | null;
is_public: number;
created_at: string;
image_mime: string | null;
[key: string]: SQLOutputValue;
}
export function isPlaylistRow(
obj: Record<string, SQLOutputValue>,
): obj is PlaylistRow {
return !!obj && typeof obj.id === "string" &&
typeof obj.user_id === "string" &&
typeof obj.title === "string" &&
typeof obj.is_public === "number" &&
typeof obj.created_at === "string";
}
export function playlistRowToApi(row: PlaylistRow): Playlist {
return {
id: row.id,
userId: row.user_id,
title: row.title,
description: row.description ?? undefined,
isPublic: Boolean(row.is_public),
createdAt: new Date(row.created_at),
imageMime: row.image_mime ?? undefined,
dumpCount: typeof row.dump_count === "number" ? row.dump_count : undefined,
};
}

View File

@@ -141,6 +141,74 @@ export class APIException extends Error {
}
}
/**
* Playlists
*/
export interface Playlist {
id: string;
userId: string;
title: string;
description?: string;
isPublic: boolean;
createdAt: Date;
imageMime?: string;
dumpCount?: number;
}
export interface PlaylistWithDumps extends Playlist {
dumps: Dump[];
}
export interface PlaylistMembership {
playlist: Playlist;
hasDump: boolean;
}
export interface CreatePlaylistRequest {
title: string;
description?: string;
isPublic: boolean;
}
export interface UpdatePlaylistRequest {
title?: string;
description?: string;
isPublic?: boolean;
}
export interface ReorderPlaylistRequest {
dumpIds: string[];
}
export function isCreatePlaylistRequest(
obj: unknown,
): obj is CreatePlaylistRequest {
return !!obj && typeof obj === "object" &&
"title" in obj && typeof obj.title === "string" &&
(!("description" in obj) || typeof obj.description === "string" ||
obj.description === null) &&
"isPublic" in obj && typeof obj.isPublic === "boolean";
}
export function isUpdatePlaylistRequest(
obj: unknown,
): obj is UpdatePlaylistRequest {
return !!obj && typeof obj === "object" &&
(!("title" in obj) || typeof obj.title === "string") &&
(!("description" in obj) || typeof obj.description === "string" ||
obj.description === null) &&
(!("isPublic" in obj) || typeof obj.isPublic === "boolean");
}
export function isReorderPlaylistRequest(
obj: unknown,
): obj is ReorderPlaylistRequest {
return !!obj && typeof obj === "object" &&
"dumpIds" in obj && Array.isArray(obj.dumpIds) &&
(obj.dumpIds as unknown[]).every((id) => typeof id === "string");
}
/**
* Request DTOs
*/