wuyiqunLu commited on
Commit
760e83d
1 Parent(s): f665131

feat: use video cover as thumbnail (#35)

Browse files

<img width="1131" alt="image"
src="https://github.com/landing-ai/vision-agent-ui/assets/132986242/4c981213-59a1-47a3-831e-a0cf529ad865">

app/api/vision-agent/route.ts CHANGED
@@ -1,8 +1,4 @@
1
- import {
2
- StreamingTextResponse,
3
- experimental_StreamData,
4
- createStreamDataTransformer,
5
- } from 'ai';
6
 
7
  // import { auth } from '@/auth';
8
  import { MessageBase } from '../../../lib/types';
@@ -76,11 +72,15 @@ export const POST = withLogging(
76
  return new StreamingTextResponse(
77
  new ReadableStream({
78
  async start(controller) {
79
- const { done, value } = await reader.read();
80
- if (!done) {
81
- const errorText = new TextDecoder().decode(value);
82
- logger.error(session, errorText, request);
83
- controller.error(new Error(`Response error: ${errorText}`));
 
 
 
 
84
  }
85
  },
86
  }),
@@ -110,7 +110,6 @@ export const POST = withLogging(
110
  if (fetchResponse.body) {
111
  const encoder = new TextEncoder();
112
  const decoder = new TextDecoder();
113
-
114
  const stream = fetchResponse.body.pipeThrough(
115
  new TransformStream({
116
  transform: async (chunk, controller) => {
 
1
+ import { StreamingTextResponse } from 'ai';
 
 
 
 
2
 
3
  // import { auth } from '@/auth';
4
  import { MessageBase } from '../../../lib/types';
 
72
  return new StreamingTextResponse(
73
  new ReadableStream({
74
  async start(controller) {
75
+ try {
76
+ const { done, value } = await reader.read();
77
+ if (!done) {
78
+ const errorText = new TextDecoder().decode(value);
79
+ logger.error(session, errorText, request);
80
+ controller.error(new Error(`Response error: ${errorText}`));
81
+ }
82
+ } catch (e) {
83
+ logger.error(session, (e as Error).message, request);
84
  }
85
  },
86
  }),
 
110
  if (fetchResponse.body) {
111
  const encoder = new TextEncoder();
112
  const decoder = new TextDecoder();
 
113
  const stream = fetchResponse.body.pipeThrough(
114
  new TransformStream({
115
  transform: async (chunk, controller) => {
components/ui/Img.tsx CHANGED
@@ -1,8 +1,10 @@
1
  'use client';
2
 
3
- import React from 'react';
4
  import Image from 'next/image';
5
  import { cn } from '@/lib/utils';
 
 
6
 
7
  const placeholder =
8
  'data:image/jpeg;base64,/9j/4AAQSkZJRgABAgEASABIAAD/4QDWRXhpZgAATU0AKgAAAAgABwEGAAMAAAABAAIAAAESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAABAAAAagEoAAMAAAABAAIAAAITAAMAAAABAAEAAIdpAAQAAAABAAAAcgAAAAAAAABIAAAAAQAAAEgAAAABAAeQAAAHAAAABDAyMjGRAQAHAAAABAECAwCgAAAHAAAABDAxMDCgAQADAAAAAQABAACgAgAEAAAAAQAAADKgAwAEAAAAAQAAADKkBgADAAAAAQAAAAAAAAAAAAD/wAARCAAyADIDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9sAQwABAQEBAQECAQECAwICAgMEAwMDAwQGBAQEBAQGBwYGBgYGBgcHBwcHBwcHCAgICAgICQkJCQkLCwsLCwsLCwsL/9sAQwECAgIDAwMFAwMFCwgGCAsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL/90ABAAE/9oADAMBAAIRAxEAPwD+nbSfDst/NnB5Ne5+G/htJMqsVOD610fw/wDCS3Lq7L3r6y0Lw7b20SgqMiluB4hpvwti2Asn51fufhVbsmQlfSCQQQjHFOIt24OKLAfEXiL4XtCjMiGvnrxH4SlsJG+UjFfqZf6Va3UZUgV84/ETwTGY3kRaVrbAfAp058//AK6T+zn/AM5/wr1uTw0BIwx3P+elM/4Roen+fyo5gP/Q/tp+G1vGIVJFer6n4lstGizM4XFeH/DzVUW3HPavmL9pX4sXvhmKV0Yqq5qbgfXeq/GjRLJipmH51yMn7Qvh2NtrTL+dfzYfGL9uqTw1PLGZjkE96+M7j/gotqtzf+WkrYz60rsD+1bwt8YtD12ZYYZlJPvXY+KlivdOMy8giv5q/wBjD9prxB8QNcgXezKxHOa/obsdZa48KRPMTuKiqA8dm09PNbjuaj/s9PSp5rtDM5B7nvUf2tfX9f8A69QB/9H+v3wLrW1RHuxmvJv2iPh43jLRZhEuSynpWN4X8Rm0lCs2BnivoKy1iy1W1ENyQcjFQB/J7+0x+x34yvtVmlsY3OScYBr418KfsLfES81xFmgkALDPBr+1rXPhR4d15jJJEj59qwtP+BXhiznE4t0BHsKEugH54fsJfsqS/Dqzt7m/TDgAnIr9j9Q1BNN0kW4OAq1yun2mk+G7fZAFG0dq868Y+LwyMit+FFtAEl1tfNb5x1NR/wBtr/fH+fwrxptbnLE5703+2p6QH//S/pMs+JjivYfDjv5Q5NePWn+uNev+HP8AVCkwPWdPdtvU1fnkk2n5j09aztP+7V6f7p+lLoBxeuu4jOCa8N1xmMjEnNe4a7/qzXh2t/6xqfQDlqKKKgD/2Q==';
@@ -18,9 +20,28 @@ const Img = React.forwardRef<
18
  });
19
  // const [isLoading, setIsLoading] = React.useState(true);
20
  const [_, startTransition] = React.useTransition();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  return (
22
  <Image
23
- src={src}
24
  placeholder={placeholder}
25
  width={dimensions.width}
26
  height={dimensions.height}
 
1
  'use client';
2
 
3
+ import React, { useEffect, useState } from 'react';
4
  import Image from 'next/image';
5
  import { cn } from '@/lib/utils';
6
+ import { toast } from 'react-hot-toast';
7
+ import { getVideoCover } from '@rajesh896/video-thumbnails-generator';
8
 
9
  const placeholder =
10
  'data:image/jpeg;base64,/9j/4AAQSkZJRgABAgEASABIAAD/4QDWRXhpZgAATU0AKgAAAAgABwEGAAMAAAABAAIAAAESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAABAAAAagEoAAMAAAABAAIAAAITAAMAAAABAAEAAIdpAAQAAAABAAAAcgAAAAAAAABIAAAAAQAAAEgAAAABAAeQAAAHAAAABDAyMjGRAQAHAAAABAECAwCgAAAHAAAABDAxMDCgAQADAAAAAQABAACgAgAEAAAAAQAAADKgAwAEAAAAAQAAADKkBgADAAAAAQAAAAAAAAAAAAD/wAARCAAyADIDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9sAQwABAQEBAQECAQECAwICAgMEAwMDAwQGBAQEBAQGBwYGBgYGBgcHBwcHBwcHCAgICAgICQkJCQkLCwsLCwsLCwsL/9sAQwECAgIDAwMFAwMFCwgGCAsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL/90ABAAE/9oADAMBAAIRAxEAPwD+nbSfDst/NnB5Ne5+G/htJMqsVOD610fw/wDCS3Lq7L3r6y0Lw7b20SgqMiluB4hpvwti2Asn51fufhVbsmQlfSCQQQjHFOIt24OKLAfEXiL4XtCjMiGvnrxH4SlsJG+UjFfqZf6Va3UZUgV84/ETwTGY3kRaVrbAfAp058//AK6T+zn/AM5/wr1uTw0BIwx3P+elM/4Roen+fyo5gP/Q/tp+G1vGIVJFer6n4lstGizM4XFeH/DzVUW3HPavmL9pX4sXvhmKV0Yqq5qbgfXeq/GjRLJipmH51yMn7Qvh2NtrTL+dfzYfGL9uqTw1PLGZjkE96+M7j/gotqtzf+WkrYz60rsD+1bwt8YtD12ZYYZlJPvXY+KlivdOMy8giv5q/wBjD9prxB8QNcgXezKxHOa/obsdZa48KRPMTuKiqA8dm09PNbjuaj/s9PSp5rtDM5B7nvUf2tfX9f8A69QB/9H+v3wLrW1RHuxmvJv2iPh43jLRZhEuSynpWN4X8Rm0lCs2BnivoKy1iy1W1ENyQcjFQB/J7+0x+x34yvtVmlsY3OScYBr418KfsLfES81xFmgkALDPBr+1rXPhR4d15jJJEj59qwtP+BXhiznE4t0BHsKEugH54fsJfsqS/Dqzt7m/TDgAnIr9j9Q1BNN0kW4OAq1yun2mk+G7fZAFG0dq868Y+LwyMit+FFtAEl1tfNb5x1NR/wBtr/fH+fwrxptbnLE5703+2p6QH//S/pMs+JjivYfDjv5Q5NePWn+uNev+HP8AVCkwPWdPdtvU1fnkk2n5j09aztP+7V6f7p+lLoBxeuu4jOCa8N1xmMjEnNe4a7/qzXh2t/6xqfQDlqKKKgD/2Q==';
 
20
  });
21
  // const [isLoading, setIsLoading] = React.useState(true);
22
  const [_, startTransition] = React.useTransition();
23
+ const [thumbnail, setThumbnail] = useState<string>('');
24
+ const isVideo =
25
+ typeof src === 'string' ? src.toLowerCase().endsWith('.mp4') : false;
26
+
27
+ useEffect(() => {
28
+ if (!isVideo) {
29
+ return;
30
+ }
31
+ const generateThumbnail = async () => {
32
+ try {
33
+ const cover = await getVideoCover(src as string);
34
+ setThumbnail(cover);
35
+ } catch (e) {
36
+ toast.error((e as Error).message);
37
+ }
38
+ };
39
+ generateThumbnail();
40
+ }, [isVideo, src]);
41
+
42
  return (
43
  <Image
44
+ src={isVideo ? thumbnail : src}
45
  placeholder={placeholder}
46
  width={dimensions.width}
47
  height={dimensions.height}
package.json CHANGED
@@ -23,6 +23,7 @@
23
  "@radix-ui/react-slot": "^1.0.2",
24
  "@radix-ui/react-switch": "^1.0.3",
25
  "@radix-ui/react-tooltip": "^1.0.7",
 
26
  "@vercel/kv": "^1.0.1",
27
  "ai": "^2.2.31",
28
  "class-variance-authority": "^0.7.0",
 
23
  "@radix-ui/react-slot": "^1.0.2",
24
  "@radix-ui/react-switch": "^1.0.3",
25
  "@radix-ui/react-tooltip": "^1.0.7",
26
+ "@rajesh896/video-thumbnails-generator": "^2.3.9",
27
  "@vercel/kv": "^1.0.1",
28
  "ai": "^2.2.31",
29
  "class-variance-authority": "^0.7.0",
pnpm-lock.yaml CHANGED
The diff for this file is too large to render. See raw diff