Some checks failed
CI / skip-ci-check (pull_request) Successful in 1m4s
CI / lint-and-type-check (pull_request) Has been cancelled
CI / python-lint (pull_request) Has been cancelled
CI / test-backend (pull_request) Has been cancelled
CI / build (pull_request) Has been cancelled
CI / secret-scanning (pull_request) Has been cancelled
CI / dependency-scan (pull_request) Has been cancelled
CI / sast-scan (pull_request) Has been cancelled
CI / workflow-summary (pull_request) Has been cancelled
Add on-demand H.264/AAC web playback (RQ, ffmpeg) with API routes and Next.js proxies; extend admin UI with WebPlaybackVideo and shared hooks. Store transcode cache beside pending-photos (WEB_VIDEO_CACHE_DIR / UPLOAD_DIR) and ignore data/web_videos. Centralize FastAPI URL helpers, optional Vite and Next base paths for subfolder deploy, and fix modal reopen by using router.replace when closing the home photo viewer. Include migration, install scripts, deployment doc updates, and CI admin build env tweak. Made-with: Cursor
68 lines
1.9 KiB
TypeScript
68 lines
1.9 KiB
TypeScript
import { Photo } from '@prisma/client';
|
|
|
|
/**
|
|
* Determines if a path is a URL (http/https) or a file system path
|
|
*/
|
|
export function isUrl(path: string): boolean {
|
|
return path.startsWith('http://') || path.startsWith('https://');
|
|
}
|
|
|
|
/**
|
|
* Check if photo is a video
|
|
*/
|
|
export function isVideo(photo: Photo): boolean {
|
|
// Handle both camelCase (Prisma client) and snake_case (direct DB access)
|
|
return (photo as any).mediaType === 'video' || (photo as any).media_type === 'video';
|
|
}
|
|
|
|
/**
|
|
* Gets the appropriate image source URL
|
|
* - URLs (SharePoint, CDN, etc.) → use directly
|
|
* - File system paths → use API proxy
|
|
* - Videos → use thumbnail endpoint (for grid display)
|
|
*/
|
|
export function getImageSrc(photo: Photo, options?: { watermark?: boolean; thumbnail?: boolean }): string {
|
|
// For videos, use thumbnail endpoint if requested (for grid display)
|
|
if (options?.thumbnail && isVideo(photo)) {
|
|
return `/api/photos/${photo.id}/image?thumbnail=true`;
|
|
}
|
|
|
|
if (isUrl(photo.path)) {
|
|
if (typeof window !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
console.log(`✅ Photo ${photo.id}: Using DIRECT access for URL:`, photo.path);
|
|
}
|
|
return photo.path;
|
|
}
|
|
|
|
const params = new URLSearchParams();
|
|
if (options?.watermark) {
|
|
params.set('watermark', 'true');
|
|
}
|
|
const query = params.toString();
|
|
|
|
if (typeof window !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
console.log(`📁 Photo ${photo.id}: Using API PROXY for file path:`, photo.path);
|
|
}
|
|
|
|
return `/api/photos/${photo.id}/image${query ? `?${query}` : ''}`;
|
|
}
|
|
|
|
/**
|
|
* Gets the appropriate video source URL
|
|
*/
|
|
export function getVideoSrc(photo: Photo): string {
|
|
if (isUrl(photo.path)) {
|
|
return photo.path;
|
|
}
|
|
return `/api/photos/${photo.id}/image`;
|
|
}
|
|
|
|
/**
|
|
* Browser-safe transcoded MP4 URL (after prepare + status=ready).
|
|
*/
|
|
export function getWebPlaybackStreamUrl(photo: Photo): string {
|
|
return `/api/photos/${photo.id}/web-playback`;
|
|
}
|
|
|
|
|