tubearr/src/db/schema/content.ts
jlightner 8150b1f6cf chore: Made content_items.channelId nullable via SQLite table recreatio…
- "src/db/schema/content.ts"
- "src/types/index.ts"
- "src/db/repositories/content-repository.ts"
- "src/services/queue.ts"
- "drizzle/0012_adhoc_nullable_channel.sql"
- "drizzle/meta/_journal.json"

GSD-Task: S01/T01
2026-04-04 05:03:40 +00:00

52 lines
2.7 KiB
TypeScript

import { sqliteTable, text, integer, real } from 'drizzle-orm/sqlite-core';
import { sql } from 'drizzle-orm';
import { channels } from './channels';
/** Individual content items (videos, audio tracks, livestreams). */
export const contentItems = sqliteTable('content_items', {
id: integer('id').primaryKey({ autoIncrement: true }),
channelId: integer('channel_id')
.references(() => channels.id, { onDelete: 'cascade' }),
title: text('title').notNull(),
platformContentId: text('platform_content_id').notNull(),
url: text('url').notNull(),
contentType: text('content_type').notNull(), // 'video' | 'audio' | 'livestream'
duration: integer('duration'), // seconds
filePath: text('file_path'),
fileSize: integer('file_size'), // bytes
format: text('format'), // container format e.g. 'mp4', 'webm', 'mp3'
qualityMetadata: text('quality_metadata', { mode: 'json' }), // actual quality info post-download
status: text('status').notNull().default('monitored'), // monitored|queued|downloading|downloaded|failed|ignored
thumbnailUrl: text('thumbnail_url'),
publishedAt: text('published_at'), // ISO datetime from platform (nullable)
downloadedAt: text('downloaded_at'), // ISO datetime when download completed (nullable)
monitored: integer('monitored', { mode: 'boolean' }).notNull().default(true), // per-item monitoring toggle
createdAt: text('created_at')
.notNull()
.default(sql`(datetime('now'))`),
updatedAt: text('updated_at')
.notNull()
.default(sql`(datetime('now'))`),
});
/** Format profiles defining preferred download quality/format settings. */
export const formatProfiles = sqliteTable('format_profiles', {
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name').notNull(),
videoResolution: text('video_resolution'), // e.g. '1080p', '720p', 'best'
audioCodec: text('audio_codec'), // e.g. 'opus', 'aac', 'mp3'
audioBitrate: text('audio_bitrate'), // e.g. '320k', '192k'
containerFormat: text('container_format'), // e.g. 'mp4', 'mkv', 'mp3'
isDefault: integer('is_default', { mode: 'boolean' }).notNull().default(false),
subtitleLanguages: text('subtitle_languages'),
embedSubtitles: integer('embed_subtitles', { mode: 'boolean' }).notNull().default(false),
embedChapters: integer('embed_chapters', { mode: 'boolean' }).notNull().default(false),
embedThumbnail: integer('embed_thumbnail', { mode: 'boolean' }).notNull().default(false),
sponsorBlockRemove: text('sponsor_block_remove'), // comma-separated categories: 'sponsor,selfpromo,interaction,intro,outro,preview,music_offtopic,filler'
createdAt: text('created_at')
.notNull()
.default(sql`(datetime('now'))`),
updatedAt: text('updated_at')
.notNull()
.default(sql`(datetime('now'))`),
});