diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..20859bb --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "static-buffet-dev", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "dev"], + "autoPort": true + } + ] +} diff --git a/.env b/.env deleted file mode 100644 index 961a1e5..0000000 --- a/.env +++ /dev/null @@ -1,41 +0,0 @@ -# Static Buffet Environment Configuration -# Copy this file to .env and configure for your environment - -# Database Configuration -# Required: PostgreSQL database URL for Drizzle ORM -# Format: postgresql://username:password@hostname:port/database -DATABASE_URL=sqlite:./local.db - -# Server Configuration -# Optional: Port for the development/production server (default: 5000) -PORT=3000 - -# Video Cache Configuration -# Optional: Directory for cached video files (default: ./video-cache) -VIDEO_CACHE_DIR=./video-cache - -# Development Settings -# Optional: Node environment (development/production) -NODE_ENV=development - -# Archive.org API Settings -# No API key required for Archive.org - these are just for reference -# ARCHIVE_API_BASE=https://archive.org -# ARCHIVE_SEARCH_ENDPOINT=/advancedsearch.php -# ARCHIVE_METADATA_ENDPOINT=/metadata - -# Performance Tuning (Optional) -# Maximum concurrent video transcoding jobs -# MAX_TRANSCODE_JOBS=3 - -# Cache TTL Settings (Optional) -# Metadata cache TTL in hours (default: 48) -# METADATA_CACHE_TTL_HOURS=48 - -# Search cache TTL in hours (default: 6) -# SEARCH_CACHE_TTL_HOURS=6 - -# Security (Production Only) -# CORS_ORIGIN=https://yourdomain.com -# RATE_LIMIT_WINDOW_MS=1000 -# RATE_LIMIT_MAX_REQUESTS=20 \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..86647b2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,21 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - name: Typecheck + run: npm run check + - name: Build + run: npm run build diff --git a/.gitignore b/.gitignore index f9ba7f8..d876dd5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,7 @@ dist .DS_Store server/public vite.config.ts.* -*.tar.gz \ No newline at end of file +*.tar.gz +.env +video-cache/ +local.db \ No newline at end of file diff --git a/client/src/components/CommandPalette.tsx b/client/src/components/CommandPalette.tsx index 19d8c5e..acf5f0b 100644 --- a/client/src/components/CommandPalette.tsx +++ b/client/src/components/CommandPalette.tsx @@ -5,6 +5,10 @@ import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, Command import { Badge } from '@/components/ui/badge'; import { useStore } from '@/lib/store'; import { useToast } from '@/hooks/use-toast'; +import { getVideoMetadata } from '@/lib/archive-api'; +import { type BrandSkin } from '@/lib/types'; + +const BRAND_SKINS: BrandSkin[] = ['testcard', 'waffle', 'ebn', 'ozzy', 'hogan', 'dx', 'maxheadroom', 'mario', 'dakota', 'blondie', 'diner']; export interface CommandPaletteProps { isOpen: boolean; @@ -24,9 +28,9 @@ interface CommandAction { } export function CommandPalette({ isOpen, onClose }: CommandPaletteProps) { - const { - setSearchQuery, - searchState, + const { + setSearchState, + searchState, queueItems, clearQueue, setDetailDrawerOpen, @@ -68,7 +72,7 @@ export function CommandPalette({ isOpen, onClose }: CommandPaletteProps) { icon: , keywords: ['clear', 'reset', 'empty', 'search'], action: () => { - setSearchQuery(''); + setSearchState({ query: '', page: 1 }); onClose(); toast({ title: 'Search cleared' }); }, @@ -120,10 +124,18 @@ export function CommandPalette({ isOpen, onClose }: CommandPaletteProps) { description: selectedVideo ? `Add "${selectedVideo.title}" to queue` : 'No video selected', icon: , keywords: ['add', 'queue', 'current', 'video', 'plus'], - action: () => { + action: async () => { if (selectedVideo) { - addToQueue(selectedVideo); - toast({ title: 'Added to queue', description: selectedVideo.title }); + try { + const metadata = await getVideoMetadata(selectedVideo.identifier); + if (!metadata.streamUrl) { + throw new Error('No playable stream URL'); + } + addToQueue(selectedVideo, metadata.streamUrl); + toast({ title: 'Added to queue', description: selectedVideo.title }); + } catch { + toast({ title: 'Could not add video', description: 'No playable stream found', variant: 'destructive' }); + } } else { toast({ title: 'No video selected', variant: 'destructive' }); } @@ -157,7 +169,7 @@ export function CommandPalette({ isOpen, onClose }: CommandPaletteProps) { icon: