feat: Added media_servers table, MediaServer type, and CRUD repository…
- "src/db/schema/media-servers.ts" - "src/db/repositories/media-server-repository.ts" - "src/types/index.ts" - "drizzle/0016_right_galactus.sql" GSD-Task: S04/T01
This commit is contained in:
parent
9e7d98c7c7
commit
6aa7e21b90
7 changed files with 1310 additions and 0 deletions
11
drizzle/0016_right_galactus.sql
Normal file
11
drizzle/0016_right_galactus.sql
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
CREATE TABLE `media_servers` (
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`name` text NOT NULL,
|
||||||
|
`type` text NOT NULL,
|
||||||
|
`url` text NOT NULL,
|
||||||
|
`token` text NOT NULL,
|
||||||
|
`library_section` text,
|
||||||
|
`enabled` integer DEFAULT true NOT NULL,
|
||||||
|
`created_at` text DEFAULT (datetime('now')) NOT NULL,
|
||||||
|
`updated_at` text DEFAULT (datetime('now')) NOT NULL
|
||||||
|
);
|
||||||
1117
drizzle/meta/0016_snapshot.json
Normal file
1117
drizzle/meta/0016_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -113,6 +113,13 @@
|
||||||
"when": 1775280800944,
|
"when": 1775280800944,
|
||||||
"tag": "0015_perfect_lethal_legion",
|
"tag": "0015_perfect_lethal_legion",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 16,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1775281783887,
|
||||||
|
"tag": "0016_right_galactus",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
137
src/db/repositories/media-server-repository.ts
Normal file
137
src/db/repositories/media-server-repository.ts
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
import { eq } from 'drizzle-orm';
|
||||||
|
import { type LibSQLDatabase } from 'drizzle-orm/libsql';
|
||||||
|
import type * as schema from '../schema/index';
|
||||||
|
import { mediaServers } from '../schema/index';
|
||||||
|
import type { MediaServer, MediaServerType } from '../../types/index';
|
||||||
|
|
||||||
|
// ── Types ──
|
||||||
|
|
||||||
|
/** Fields needed to create a new media server. */
|
||||||
|
export interface CreateMediaServerData {
|
||||||
|
name: string;
|
||||||
|
type: MediaServerType;
|
||||||
|
url: string;
|
||||||
|
token: string;
|
||||||
|
librarySection?: string | null;
|
||||||
|
enabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Fields that can be updated on an existing media server. */
|
||||||
|
export interface UpdateMediaServerData {
|
||||||
|
name?: string;
|
||||||
|
type?: MediaServerType;
|
||||||
|
url?: string;
|
||||||
|
token?: string;
|
||||||
|
librarySection?: string | null;
|
||||||
|
enabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Db = LibSQLDatabase<typeof schema>;
|
||||||
|
|
||||||
|
// ── Repository Functions ──
|
||||||
|
|
||||||
|
/** Insert a new media server. Returns the created row. */
|
||||||
|
export async function createMediaServer(
|
||||||
|
db: Db,
|
||||||
|
data: CreateMediaServerData
|
||||||
|
): Promise<MediaServer> {
|
||||||
|
const result = await db
|
||||||
|
.insert(mediaServers)
|
||||||
|
.values({
|
||||||
|
name: data.name,
|
||||||
|
type: data.type,
|
||||||
|
url: data.url,
|
||||||
|
token: data.token,
|
||||||
|
librarySection: data.librarySection ?? null,
|
||||||
|
enabled: data.enabled ?? true,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return mapRow(result[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get all media servers, ordered by name. */
|
||||||
|
export async function getAllMediaServers(db: Db): Promise<MediaServer[]> {
|
||||||
|
const rows = await db
|
||||||
|
.select()
|
||||||
|
.from(mediaServers)
|
||||||
|
.orderBy(mediaServers.name);
|
||||||
|
|
||||||
|
return rows.map(mapRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a media server by ID. Returns null if not found. */
|
||||||
|
export async function getMediaServerById(
|
||||||
|
db: Db,
|
||||||
|
id: number
|
||||||
|
): Promise<MediaServer | null> {
|
||||||
|
const rows = await db
|
||||||
|
.select()
|
||||||
|
.from(mediaServers)
|
||||||
|
.where(eq(mediaServers.id, id))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
return rows.length > 0 ? mapRow(rows[0]) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get all enabled media servers. */
|
||||||
|
export async function getEnabledMediaServers(
|
||||||
|
db: Db
|
||||||
|
): Promise<MediaServer[]> {
|
||||||
|
const rows = await db
|
||||||
|
.select()
|
||||||
|
.from(mediaServers)
|
||||||
|
.where(eq(mediaServers.enabled, true));
|
||||||
|
|
||||||
|
return rows.map(mapRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a media server. Sets updatedAt to current time.
|
||||||
|
* Returns updated server or null if not found.
|
||||||
|
*/
|
||||||
|
export async function updateMediaServer(
|
||||||
|
db: Db,
|
||||||
|
id: number,
|
||||||
|
data: UpdateMediaServerData
|
||||||
|
): Promise<MediaServer | null> {
|
||||||
|
const result = await db
|
||||||
|
.update(mediaServers)
|
||||||
|
.set({
|
||||||
|
...data,
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
})
|
||||||
|
.where(eq(mediaServers.id, id))
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return result.length > 0 ? mapRow(result[0]) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Delete a media server by ID. Returns true if a row was deleted. */
|
||||||
|
export async function deleteMediaServer(
|
||||||
|
db: Db,
|
||||||
|
id: number
|
||||||
|
): Promise<boolean> {
|
||||||
|
const result = await db
|
||||||
|
.delete(mediaServers)
|
||||||
|
.where(eq(mediaServers.id, id))
|
||||||
|
.returning({ id: mediaServers.id });
|
||||||
|
|
||||||
|
return result.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Row Mapping ──
|
||||||
|
|
||||||
|
function mapRow(row: typeof mediaServers.$inferSelect): MediaServer {
|
||||||
|
return {
|
||||||
|
id: row.id,
|
||||||
|
name: row.name,
|
||||||
|
type: row.type as MediaServerType,
|
||||||
|
url: row.url,
|
||||||
|
token: row.token,
|
||||||
|
librarySection: row.librarySection,
|
||||||
|
enabled: row.enabled,
|
||||||
|
createdAt: row.createdAt,
|
||||||
|
updatedAt: row.updatedAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -6,3 +6,4 @@ export { downloadHistory } from './history';
|
||||||
export { notificationSettings } from './notifications';
|
export { notificationSettings } from './notifications';
|
||||||
export { platformSettings } from './platform-settings';
|
export { platformSettings } from './platform-settings';
|
||||||
export { playlists, contentPlaylist } from './playlists';
|
export { playlists, contentPlaylist } from './playlists';
|
||||||
|
export { mediaServers } from './media-servers';
|
||||||
|
|
|
||||||
19
src/db/schema/media-servers.ts
Normal file
19
src/db/schema/media-servers.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
|
||||||
|
import { sql } from 'drizzle-orm';
|
||||||
|
|
||||||
|
/** Media server connections for triggering library scans (Plex, Jellyfin). */
|
||||||
|
export const mediaServers = sqliteTable('media_servers', {
|
||||||
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||||
|
name: text('name').notNull(),
|
||||||
|
type: text('type').notNull(), // 'plex' | 'jellyfin'
|
||||||
|
url: text('url').notNull(),
|
||||||
|
token: text('token').notNull(),
|
||||||
|
librarySection: text('library_section'), // nullable — Plex section ID or Jellyfin library ID
|
||||||
|
enabled: integer('enabled', { mode: 'boolean' }).notNull().default(true),
|
||||||
|
createdAt: text('created_at')
|
||||||
|
.notNull()
|
||||||
|
.default(sql`(datetime('now'))`),
|
||||||
|
updatedAt: text('updated_at')
|
||||||
|
.notNull()
|
||||||
|
.default(sql`(datetime('now'))`),
|
||||||
|
});
|
||||||
|
|
@ -196,6 +196,24 @@ export interface SystemConfig {
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const MediaServerType = {
|
||||||
|
Plex: 'plex',
|
||||||
|
Jellyfin: 'jellyfin',
|
||||||
|
} as const;
|
||||||
|
export type MediaServerType = (typeof MediaServerType)[keyof typeof MediaServerType];
|
||||||
|
|
||||||
|
export interface MediaServer {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
type: MediaServerType;
|
||||||
|
url: string;
|
||||||
|
token: string;
|
||||||
|
librarySection: string | null;
|
||||||
|
enabled: boolean;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Playlist {
|
export interface Playlist {
|
||||||
id: number;
|
id: number;
|
||||||
channelId: number;
|
channelId: number;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue