feat: video support + Memories music choices
Videos: - Worker ingests .mp4 .mov .m4v .avi .mkv .webm .3gp via ffmpeg/ffprobe (frame extract → sharp resize, same thumbnail pipeline as photos) - DB: media_type + duration columns with migration - GET /api/videos returns all indexed videos - GET /api/photos/:id/video streams original file with Range support for browser seek/scrub - VideosView: 16:9 thumbnail grid, play-button overlay, duration badge - Full-screen player: native <video controls>, ESC to close - Library tab now excludes videos (dedicated Videos tab) - Dockerfile: adds ffmpeg to runtime stage Memories music choices: - Three styles: ⚡ Upbeat (G-major, default), 🌙 Dreamy (A-minor), 🎬 Cinematic (D-minor, dual-oscillator richness) - Shared Web Audio engine (reverb, LPF, pad + melody) - Style picker in header; active style icon shown in slideshow bar Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
7ed18f616f
commit
6b4bd88943
8 files changed
+608
-193
No files matched your search
@@ -10,6 +10,7 @@ import SettingsPanel from './components/SettingsPanel.jsx';
|
||||
import { lazy, Suspense } from 'react';
|
||||
const MapView = lazy(() => import('./components/MapView.jsx'));
|
||||
const MemoriesView = lazy(() => import('./components/MemoriesView.jsx'));
|
||||
const VideosView = lazy(() => import('./components/VideosView.jsx'));
|
||||
|
||||
const EMPTY_FILTERS = { q: '', from: '', to: '', tagId: '', collectionId: '', starred: '', city: '' };
|
||||
const SIDEBAR_W = 210;
|
||||
@@ -126,6 +127,7 @@ export default function App() {
|
||||
</span>
|
||||
)}
|
||||
</Tab>
|
||||
<Tab active={view === 'videos'} onClick={() => setView('videos')}>Videos</Tab>
|
||||
<Tab active={view === 'memories'} onClick={() => setView('memories')}>Memories</Tab>
|
||||
<Tab active={view === 'map'} onClick={() => setView('map')}>Map</Tab>
|
||||
<Tab active={view === 'duplicates'} onClick={() => setView('duplicates')}>
|
||||
@@ -216,6 +218,13 @@ export default function App() {
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Videos */}
|
||||
{view === 'videos' && (
|
||||
<Suspense fallback={<p style={{ padding: 24, color: '#777', fontSize: 13 }}>Loading…</p>}>
|
||||
<VideosView />
|
||||
</Suspense>
|
||||
)}
|
||||
|
||||
{/* Memories */}
|
||||
{view === 'memories' && (
|
||||
<Suspense fallback={<p style={{ padding: 24, color: '#777', fontSize: 13 }}>Loading…</p>}>
|
||||
|
||||
Reference in new issue
Block a user