Tonne of debugging - getting ready for the production builds
This commit is contained in:
329
shared/types/media.ts
Normal file
329
shared/types/media.ts
Normal file
@@ -0,0 +1,329 @@
|
||||
// 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' },
|
||||
};
|
||||
Reference in New Issue
Block a user