diff --git a/src/db/repositories/system-config-repository.ts b/src/db/repositories/system-config-repository.ts index d9276cc..4be9d8f 100644 --- a/src/db/repositories/system-config-repository.ts +++ b/src/db/repositories/system-config-repository.ts @@ -10,6 +10,7 @@ type Db = LibSQLDatabase; export const APP_CHECK_INTERVAL = 'app.check_interval'; export const APP_CONCURRENT_DOWNLOADS = 'app.concurrent_downloads'; +export const YTDLP_LAST_UPDATED = 'ytdlp.last_updated'; // ── Read / Write ── diff --git a/src/server/routes/system.ts b/src/server/routes/system.ts index 80ecbb9..7090e2d 100644 --- a/src/server/routes/system.ts +++ b/src/server/routes/system.ts @@ -5,15 +5,18 @@ import { fileURLToPath } from 'node:url'; import { randomUUID } from 'node:crypto'; import { eq } from 'drizzle-orm'; import { appConfig } from '../../config/index'; -import type { SystemStatusResponse, ApiKeyResponse, AppSettingsResponse } from '../../types/api'; +import type { SystemStatusResponse, ApiKeyResponse, AppSettingsResponse, YtDlpStatusResponse, YtDlpUpdateResponse } from '../../types/api'; import { systemConfig } from '../../db/schema/index'; import { API_KEY_DB_KEY } from '../middleware/auth'; import { getAppSettings, + getAppSetting, setAppSetting, APP_CHECK_INTERVAL, APP_CONCURRENT_DOWNLOADS, + YTDLP_LAST_UPDATED, } from '../../db/repositories/system-config-repository'; +import { getYtDlpVersion, updateYtDlp } from '../../sources/yt-dlp'; import os from 'node:os'; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -179,4 +182,44 @@ export async function systemRoutes(fastify: FastifyInstance): Promise { return response; }); + + // ── yt-dlp Status & Update ── + + /** + * GET /api/v1/system/ytdlp/status — Current yt-dlp version and last-updated timestamp. + */ + fastify.get('/api/v1/system/ytdlp/status', async (_request, _reply) => { + const db = fastify.db; + const [version, lastUpdated] = await Promise.all([ + getYtDlpVersion(), + getAppSetting(db, YTDLP_LAST_UPDATED), + ]); + + const response: YtDlpStatusResponse = { version, lastUpdated }; + return response; + }); + + /** + * POST /api/v1/system/ytdlp/update — Trigger a yt-dlp update and persist the timestamp. + */ + fastify.post('/api/v1/system/ytdlp/update', async (request, _reply) => { + const db = fastify.db; + const result = await updateYtDlp(); + + const lastUpdated = new Date().toISOString(); + await setAppSetting(db, YTDLP_LAST_UPDATED, lastUpdated); + + request.log.info( + { updated: result.updated, version: result.version, previousVersion: result.previousVersion }, + '[system] yt-dlp update check completed' + ); + + const response: YtDlpUpdateResponse = { + updated: result.updated, + version: result.version, + previousVersion: result.previousVersion, + lastUpdated, + }; + return response; + }); } diff --git a/src/types/api.ts b/src/types/api.ts index 247f233..0ba6f94 100644 --- a/src/types/api.ts +++ b/src/types/api.ts @@ -80,3 +80,19 @@ export interface AppSettingsResponse { export type ChannelWithCounts = import('./index').Channel & { contentCounts: ContentCounts; }; + +// ── yt-dlp Status ── + +/** Response shape for GET /api/v1/system/ytdlp/status. */ +export interface YtDlpStatusResponse { + version: string | null; + lastUpdated: string | null; +} + +/** Response shape for POST /api/v1/system/ytdlp/update. */ +export interface YtDlpUpdateResponse { + updated: boolean; + version: string | null; + previousVersion: string | null; + lastUpdated: string; +}