import { sql } from "drizzle-orm"; import { pgTable, text, varchar, integer, timestamp, boolean, real, pgEnum, jsonb, index } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { z } from "zod"; // Enums for better type safety export const contentTypeEnum = pgEnum('content_type', ['video', 'oddaja', 'music_video', 'documentary', 'live']); export const genreEnum = pgEnum('genre', ['volksmusik', 'schlager', 'pop', 'rock', 'country', 'instrumental', 'dance', 'other']); export const videos = pgTable("videos", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), title: text("title").notNull(), artist: text("artist"), // Izvajalec/Artist name description: text("description").default("").notNull(), filename: text("filename"), // Original file name from Bunny.net episodeNumber: integer("episode_number"), // For shows like "Folx Stadl" episodeTitle: text("episode_title"), // Episode title for shows thumbnailUrl: text("thumbnail_url").notNull(), customThumbnailUrl: text("custom_thumbnail_url"), faceCenterPosition: text("face_center_position"), // CSS object-position for face centering facesDetected: integer("faces_detected").default(0), // Number of faces detected faceConfidence: real("face_confidence").default(0), // Confidence score of primary face videoUrl: text("video_url").notNull(), videoUrlMp4: text("video_url_mp4"), videoUrlIframe: text("video_url_iframe"), duration: integer("duration").notNull(), // in seconds views: integer("views").notNull().default(0), category: text("category").default("").notNull(), contentType: contentTypeEnum("content_type").default('video').notNull(), genre: genreEnum("genre").default('other').notNull(), tags: text("tags").array().default([]).notNull(), isPublic: boolean("is_public").default(true).notNull(), uploadStatus: text("upload_status").default("pending").notNull(), // pending, processing, completed, failed originalFileName: text("original_file_name"), fileSize: integer("file_size"), // in bytes bitrate: integer("bitrate"), // in kbps resolution: text("resolution"), // e.g., "1920x1080" format: text("format"), // e.g., "mp4", "avi", "mov" encoding: text("encoding"), // e.g., "h264", "h265" createdAt: timestamp("created_at").notNull().default(sql`CURRENT_TIMESTAMP`), updatedAt: timestamp("updated_at").notNull().default(sql`CURRENT_TIMESTAMP`), }); // User table for authentication and video ownership export const users = pgTable("users", { id: varchar("id").primaryKey(), username: varchar("username", { length: 50 }).notNull().unique(), email: varchar("email", { length: 255 }).notNull().unique(), passwordHash: varchar("password_hash", { length: 255 }).notNull(), firstName: varchar("first_name", { length: 100 }), lastName: varchar("last_name", { length: 100 }), avatar: text("avatar"), isActive: boolean("is_active").default(true).notNull(), isAdmin: boolean("is_admin").default(false), isSuperAdmin: boolean("is_super_admin").default(false).notNull(), createdAt: timestamp("created_at").notNull().default(sql`CURRENT_TIMESTAMP`), updatedAt: timestamp("updated_at").notNull().default(sql`CURRENT_TIMESTAMP`), }); // Video uploads table for tracking upload progress export const videoUploads = pgTable("video_uploads", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id").notNull().references(() => users.id), videoId: varchar("video_id").references(() => videos.id), originalFileName: text("original_file_name").notNull(), fileSize: integer("file_size").notNull(), mimeType: text("mime_type").notNull(), uploadStatus: text("upload_status").default("uploading").notNull(), // uploading, processing, completed, failed uploadProgress: real("upload_progress").default(0).notNull(), // 0-1 errorMessage: text("error_message"), uploadUrl: text("upload_url"), // presigned URL for upload createdAt: timestamp("created_at").notNull().default(sql`CURRENT_TIMESTAMP`), updatedAt: timestamp("updated_at").notNull().default(sql`CURRENT_TIMESTAMP`), }); // Categories table for better organization export const categories = pgTable("categories", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), name: text("name").notNull().unique(), description: text("description"), color: varchar("color", { length: 7 }).default("#000000").notNull(), // hex color createdAt: timestamp("created_at").notNull().default(sql`CURRENT_TIMESTAMP`), }); // Tags table for better organization export const tags = pgTable("tags", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), name: text("name").notNull().unique(), useCount: integer("use_count").default(0).notNull(), createdAt: timestamp("created_at").notNull().default(sql`CURRENT_TIMESTAMP`), }); // Video tags junction table export const videoTags = pgTable("video_tags", { videoId: varchar("video_id").notNull().references(() => videos.id, { onDelete: "cascade" }), tagId: varchar("tag_id").notNull().references(() => tags.id, { onDelete: "cascade" }), }); // Session storage table for Replit Auth export const sessions = pgTable("sessions", { sid: varchar("sid").primaryKey(), sess: jsonb("sess").notNull(), expire: timestamp("expire").notNull(), }, (table) => [ index("IDX_session_expire").on(table.expire) ]); // Video ads/spots table for storing advertisement metadata export const videoAds = pgTable("video_ads", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), videoId: varchar("video_id").notNull().references(() => videos.id, { onDelete: "cascade" }), adType: text("ad_type").notNull(), // preroll, midroll, postroll adUrl: text("ad_url").notNull(), // VAST URL or direct video URL adTitle: text("ad_title"), adDuration: integer("ad_duration"), // in seconds position: integer("position").default(0), // for midroll ads - position in video (seconds) skipAfter: integer("skip_after"), // seconds after which ad can be skipped vastTag: text("vast_tag"), // VAST XML tag URL adNetwork: text("ad_network"), // network name (e.g., Google AdX, Publift) isActive: boolean("is_active").default(true).notNull(), priority: integer("priority").default(1), // ad priority (1 = highest) createdAt: timestamp("created_at").notNull().default(sql`CURRENT_TIMESTAMP`), updatedAt: timestamp("updated_at").notNull().default(sql`CURRENT_TIMESTAMP`), }); // Schemas for form validation and API export const insertVideoSchema = createInsertSchema(videos).omit({ id: true, createdAt: true, updatedAt: true, }); export const updateVideoSchema = createInsertSchema(videos).omit({ id: true, createdAt: true, updatedAt: true, }).partial(); export const insertUserSchema = createInsertSchema(users).omit({ id: true, createdAt: true, updatedAt: true, }); export const insertVideoUploadSchema = createInsertSchema(videoUploads).omit({ id: true, createdAt: true, updatedAt: true, }); export const insertCategorySchema = createInsertSchema(categories).omit({ id: true, createdAt: true, }); export const insertTagSchema = createInsertSchema(tags).omit({ id: true, createdAt: true, }); export const insertVideoAdSchema = createInsertSchema(videoAds).omit({ id: true, createdAt: true, updatedAt: true, }); // Type exports export type Video = typeof videos.$inferSelect; export type InsertVideo = z.infer; export type UpdateVideo = z.infer; export type VideoAd = typeof videoAds.$inferSelect; export type InsertVideoAd = z.infer; export type User = typeof users.$inferSelect; export type InsertUser = z.infer; export type VideoUpload = typeof videoUploads.$inferSelect; export type InsertVideoUpload = z.infer; export type Category = typeof categories.$inferSelect; export type InsertCategory = z.infer; export type Tag = typeof tags.$inferSelect; export type InsertTag = z.infer;