330 lines
7.7 KiB
TypeScript
330 lines
7.7 KiB
TypeScript
// Video types
|
|
export interface MediaVideo {
|
|
id: number;
|
|
path: string;
|
|
filename: string;
|
|
producer: string | null;
|
|
creator: string | null;
|
|
title: string | null;
|
|
durationSeconds: number | null;
|
|
quality: string | null;
|
|
orientation: 'H' | 'V' | null;
|
|
hasAudio: boolean;
|
|
fileSize: number | null;
|
|
fileHash: string | null;
|
|
lastValidated: Date | null;
|
|
isValid: boolean;
|
|
thumbnailPath: string | null;
|
|
createdAt: Date;
|
|
tags: string[] | null;
|
|
}
|
|
|
|
export interface MediaVideoFilters {
|
|
producer?: string;
|
|
creator?: string;
|
|
orientation?: 'H' | 'V';
|
|
hasAudio?: boolean;
|
|
isValid?: boolean;
|
|
search?: string;
|
|
limit?: number;
|
|
offset?: number;
|
|
}
|
|
|
|
export interface MediaVideoStats {
|
|
totalVideos: number;
|
|
totalDuration: number;
|
|
byProducer: Record<string, number>;
|
|
byOrientation: { H: number; V: number };
|
|
byQuality: Record<string, number>;
|
|
}
|
|
|
|
// Compilation types
|
|
export interface Compilation {
|
|
id: number;
|
|
filename: string;
|
|
path: string | null;
|
|
durationSeconds: number | null;
|
|
videoIds: number[];
|
|
settings: CompilationSettings;
|
|
createdAt: Date;
|
|
}
|
|
|
|
export interface CompilationSettings {
|
|
targetDuration: number;
|
|
middleOverlaySource: string;
|
|
columns: {
|
|
left: number[];
|
|
center: number[];
|
|
right: number[];
|
|
};
|
|
}
|
|
|
|
// Job types
|
|
export type JobType = 'compilation' | 'scan' | 'organize';
|
|
export type JobStatus = 'pending' | 'running' | 'completed' | 'failed';
|
|
|
|
export interface Job {
|
|
id: number;
|
|
type: JobType;
|
|
status: JobStatus;
|
|
progress: number;
|
|
log: string | null;
|
|
params: Record<string, unknown> | null;
|
|
startedAt: Date | null;
|
|
completedAt: Date | null;
|
|
createdAt: Date;
|
|
}
|
|
|
|
export interface CreateJobParams {
|
|
type: JobType;
|
|
params?: Record<string, unknown>;
|
|
}
|
|
|
|
// API response types
|
|
export interface PaginatedResponse<T> {
|
|
data: T[];
|
|
total: number;
|
|
limit: number;
|
|
offset: number;
|
|
}
|
|
|
|
export interface ApiError {
|
|
error: string;
|
|
message: string;
|
|
statusCode: number;
|
|
}
|
|
|
|
// ============================================
|
|
// Public Gallery Types
|
|
// ============================================
|
|
|
|
export type MediaCategory = 'compilations' | 'curated' | 'playback' | 'videos';
|
|
|
|
export interface SharedMedia {
|
|
id: number;
|
|
path: string;
|
|
filename: string;
|
|
category: MediaCategory;
|
|
durationSeconds: number | null;
|
|
quality: string | null;
|
|
orientation: 'H' | 'V' | null;
|
|
thumbnailPath: string | null;
|
|
viewCount: number;
|
|
upvoteCount: number;
|
|
commentCount: number;
|
|
totalWatchTime: number;
|
|
createdAt: Date;
|
|
userUpvoted?: boolean;
|
|
// Locked video system
|
|
isLocked: boolean;
|
|
lockedAt: Date | null;
|
|
lockedBy: number | null;
|
|
}
|
|
|
|
export interface SharedMediaComment {
|
|
id: number;
|
|
mediaId: number;
|
|
sessionId: string;
|
|
content: string;
|
|
createdAt: Date;
|
|
}
|
|
|
|
export interface UpvoteResponse {
|
|
upvoted: boolean;
|
|
upvoteCount: number;
|
|
}
|
|
|
|
export interface ViewResponse {
|
|
viewCount: number;
|
|
isNewView: boolean;
|
|
}
|
|
|
|
export interface MediaStats {
|
|
viewCount: number;
|
|
upvoteCount: number;
|
|
commentCount: number;
|
|
totalWatchTime: number;
|
|
}
|
|
|
|
export interface SharedMediaFilters {
|
|
category?: MediaCategory;
|
|
sort?: 'recent' | 'popular' | 'most_viewed';
|
|
limit?: number;
|
|
offset?: number;
|
|
}
|
|
|
|
// ============================================
|
|
// Activity Feed Types
|
|
// ============================================
|
|
|
|
export type ActivityEventType = 'upvote' | 'comment' | 'view' | 'new_media';
|
|
|
|
export interface ActivityEvent {
|
|
type: ActivityEventType;
|
|
mediaId: number;
|
|
mediaTitle: string;
|
|
username: string;
|
|
timestamp: string;
|
|
preview?: string;
|
|
category?: MediaCategory;
|
|
}
|
|
|
|
export interface ActivityStreamMessage {
|
|
type: 'initial' | 'event';
|
|
events?: ActivityEvent[];
|
|
event?: ActivityEvent;
|
|
}
|
|
|
|
// ============================================
|
|
// Achievement & Dashboard Types
|
|
// ============================================
|
|
|
|
export type AchievementRarity = 'common' | 'uncommon' | 'rare' | 'epic' | 'legendary';
|
|
|
|
export interface Achievement {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
icon: string;
|
|
rarity: AchievementRarity;
|
|
threshold: number;
|
|
isUnlocked: boolean;
|
|
unlockedAt?: Date;
|
|
progress: number;
|
|
currentValue: number;
|
|
}
|
|
|
|
export interface UserStats {
|
|
totalWatchTimeSeconds: number;
|
|
totalVideosWatched: number;
|
|
totalUpvotesGiven: number;
|
|
totalCommentsMade: number;
|
|
totalCompletions: number;
|
|
currentDayStreak: number;
|
|
longestDayStreak: number;
|
|
longestSingleSession: number;
|
|
}
|
|
|
|
export interface DashboardData {
|
|
stats: UserStats;
|
|
recentAchievements: UnlockedAchievement[];
|
|
achievementProgress: {
|
|
unlocked: number;
|
|
total: number;
|
|
byCategory: Record<string, { unlocked: number; total: number }>;
|
|
};
|
|
achievements: Achievement[];
|
|
}
|
|
|
|
export interface UnlockedAchievement {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
icon: string;
|
|
rarity: AchievementRarity;
|
|
unlockedAt: Date;
|
|
}
|
|
|
|
export interface CompletionResponse {
|
|
success: boolean;
|
|
totalCompletions: number;
|
|
unlockedAchievements: UnlockedAchievement[];
|
|
}
|
|
|
|
export interface AchievementsResponse {
|
|
achievements: Achievement[];
|
|
byCategory: Record<string, Achievement[]>;
|
|
summary: {
|
|
unlocked: number;
|
|
total: number;
|
|
};
|
|
}
|
|
|
|
// ============================================
|
|
// Digest Prompt Types
|
|
// ============================================
|
|
|
|
export {
|
|
DESCRIPTION_STAGE_PROMPT,
|
|
TAGGING_STAGE_PROMPT,
|
|
CLASSIFICATION_STAGE_PROMPT,
|
|
SYNTHESIS_STAGE_TEMPLATE,
|
|
PROMPT_PLACEHOLDERS,
|
|
SYNTHESIS_PROMPT_INFO,
|
|
DEFAULT_PROMPTS,
|
|
type DefaultPrompts,
|
|
} from './prompts.js';
|
|
|
|
// ============================================
|
|
// Utility Functions
|
|
// ============================================
|
|
|
|
/**
|
|
* Format duration in seconds to H:MM:SS or M:SS format
|
|
* Used for video player displays
|
|
*/
|
|
export function formatDuration(seconds: number): string {
|
|
const hours = Math.floor(seconds / 3600);
|
|
const minutes = Math.floor((seconds % 3600) / 60);
|
|
const secs = Math.floor(seconds % 60);
|
|
if (hours > 0) {
|
|
return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
|
}
|
|
return `${minutes}:${secs.toString().padStart(2, '0')}`;
|
|
}
|
|
|
|
/**
|
|
* Format duration in seconds to compact H:MM or Xm format
|
|
* Used for list displays where space is limited
|
|
*/
|
|
export function formatDurationCompact(seconds: number): string {
|
|
const hours = Math.floor(seconds / 3600);
|
|
const minutes = Math.floor((seconds % 3600) / 60);
|
|
if (hours > 0) {
|
|
return `${hours}:${minutes.toString().padStart(2, '0')}`;
|
|
}
|
|
return `${minutes}m`;
|
|
}
|
|
|
|
/**
|
|
* Format time in seconds to M:SS format
|
|
* Used for scene/clip time displays
|
|
*/
|
|
export function formatTime(seconds: number): string {
|
|
const mins = Math.floor(seconds / 60);
|
|
const secs = Math.floor(seconds % 60);
|
|
return `${mins}:${secs.toString().padStart(2, '0')}`;
|
|
}
|
|
|
|
// ============================================
|
|
// Reaction Types
|
|
// ============================================
|
|
|
|
export const MEDIA_REACTION_TYPES = ['like', 'love', 'laugh', 'wow', 'sad', 'angry'] as const;
|
|
export type MediaReactionType = typeof MEDIA_REACTION_TYPES[number];
|
|
|
|
export interface MediaReaction {
|
|
id: number;
|
|
userId: number;
|
|
mediaId: number;
|
|
reactionType: MediaReactionType;
|
|
timestamp: number; // Video timestamp in seconds where reaction occurred
|
|
createdAt: Date;
|
|
}
|
|
|
|
// ============================================
|
|
// Shared Constants
|
|
// ============================================
|
|
|
|
/**
|
|
* Public directory badge configuration
|
|
* Maps directory types to display labels and colors
|
|
*/
|
|
export const PUBLIC_DIR_BADGES: Record<string, { label: string; color: string }> = {
|
|
videos: { label: 'V', color: 'bg-green-600' },
|
|
curated: { label: 'C', color: 'bg-blue-600' },
|
|
compilations: { label: 'P', color: 'bg-purple-600' },
|
|
playback: { label: 'B', color: 'bg-indigo-600' },
|
|
highlights: { label: 'H', color: 'bg-pink-600' },
|
|
};
|