Spaces:
Sleeping
Sleeping
Jon Taylor
commited on
Commit
•
b3de037
1
Parent(s):
71f583a
deps
Browse files- Dockerfile +2 -2
- app/bot.py +16 -11
- frontend/app/components/Call.js +41 -11
- frontend/app/components/CreateRoom.js +42 -1
- frontend/package.json +2 -0
- frontend/tailwind.config.js +1 -1
- frontend/yarn.lock +17 -0
Dockerfile
CHANGED
@@ -51,11 +51,11 @@ RUN cd frontend && npm install
|
|
51 |
|
52 |
# Copy frontend app and build
|
53 |
COPY --chown=user frontend/ frontend/
|
54 |
-
|
55 |
|
56 |
# Copy everything else
|
57 |
COPY --chown=user app/ app/
|
58 |
COPY --chown=user server.py server.py
|
59 |
|
60 |
-
|
61 |
CMD ["python3", "server.py"]
|
|
|
51 |
|
52 |
# Copy frontend app and build
|
53 |
COPY --chown=user frontend/ frontend/
|
54 |
+
RUN cd frontend && npm run build
|
55 |
|
56 |
# Copy everything else
|
57 |
COPY --chown=user app/ app/
|
58 |
COPY --chown=user server.py server.py
|
59 |
|
60 |
+
ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libtcmalloc.so.4
|
61 |
CMD ["python3", "server.py"]
|
app/bot.py
CHANGED
@@ -30,13 +30,14 @@ class DailyVision(EventHandler):
|
|
30 |
self.__pipeline = Pipeline
|
31 |
self.__camera = None
|
32 |
self.__time = time.time()
|
33 |
-
self.__queue = queue.Queue()
|
34 |
self.__app_quit = False
|
35 |
self.__image_buffer = None
|
36 |
self.__bot_name = bot_name
|
37 |
self.__room_url = room_url
|
38 |
self.__room_name = room_name
|
39 |
self.__expiration = expiration
|
|
|
40 |
self.__idle = idle
|
41 |
|
42 |
# Create the pipeline (this might take a moment)
|
@@ -101,7 +102,7 @@ class DailyVision(EventHandler):
|
|
101 |
})
|
102 |
|
103 |
def process_frames(self):
|
104 |
-
params =
|
105 |
|
106 |
while not self.__app_quit:
|
107 |
# Is anyone watching?
|
@@ -114,6 +115,7 @@ class DailyVision(EventHandler):
|
|
114 |
self.logger.info(f"Expiration timer exceeded. Exiting...")
|
115 |
self.__app_quit = True
|
116 |
break
|
|
|
117 |
try:
|
118 |
#video_frame = self.__queue.get(timeout=5)
|
119 |
video_frame = self.__image_buffer
|
@@ -124,7 +126,8 @@ class DailyVision(EventHandler):
|
|
124 |
self.__camera.write_frame(result_image.tobytes())
|
125 |
except queue.Empty:
|
126 |
pass
|
127 |
-
|
|
|
128 |
def on_video_frame(self, participant_id, video_frame):
|
129 |
# Process ~15 frames per second (considering incoming frames at 30fps).
|
130 |
if time.time() - self.__time > float(os.getenv("FPS_CAP", 0.0333)):
|
@@ -132,16 +135,18 @@ class DailyVision(EventHandler):
|
|
132 |
self.__image_buffer = video_frame
|
133 |
#self.__queue.put(video_frame)
|
134 |
|
135 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
136 |
self.__client.send_app_message(
|
137 |
{
|
138 |
-
"
|
139 |
-
"reaction": {
|
140 |
-
"emoji": emoji,
|
141 |
-
"room": "main-room",
|
142 |
-
"sessionId": "bot",
|
143 |
-
"id": time.time(),
|
144 |
-
},
|
145 |
}
|
146 |
)
|
147 |
|
|
|
30 |
self.__pipeline = Pipeline
|
31 |
self.__camera = None
|
32 |
self.__time = time.time()
|
33 |
+
#self.__queue = queue.Queue()
|
34 |
self.__app_quit = False
|
35 |
self.__image_buffer = None
|
36 |
self.__bot_name = bot_name
|
37 |
self.__room_url = room_url
|
38 |
self.__room_name = room_name
|
39 |
self.__expiration = expiration
|
40 |
+
self.__params = Pipeline.InputParams()
|
41 |
self.__idle = idle
|
42 |
|
43 |
# Create the pipeline (this might take a moment)
|
|
|
102 |
})
|
103 |
|
104 |
def process_frames(self):
|
105 |
+
params = self.__params
|
106 |
|
107 |
while not self.__app_quit:
|
108 |
# Is anyone watching?
|
|
|
115 |
self.logger.info(f"Expiration timer exceeded. Exiting...")
|
116 |
self.__app_quit = True
|
117 |
break
|
118 |
+
"""
|
119 |
try:
|
120 |
#video_frame = self.__queue.get(timeout=5)
|
121 |
video_frame = self.__image_buffer
|
|
|
126 |
self.__camera.write_frame(result_image.tobytes())
|
127 |
except queue.Empty:
|
128 |
pass
|
129 |
+
"""
|
130 |
+
|
131 |
def on_video_frame(self, participant_id, video_frame):
|
132 |
# Process ~15 frames per second (considering incoming frames at 30fps).
|
133 |
if time.time() - self.__time > float(os.getenv("FPS_CAP", 0.0333)):
|
|
|
135 |
self.__image_buffer = video_frame
|
136 |
#self.__queue.put(video_frame)
|
137 |
|
138 |
+
def on_app_message(self, message, sender):
|
139 |
+
# Update pipeline settings based on message data
|
140 |
+
print(message)
|
141 |
+
self.__params = self.__pipeline.InputParams(**message)
|
142 |
+
print(self.__params)
|
143 |
+
#print(self.__pipeline.Info())
|
144 |
+
return
|
145 |
+
|
146 |
+
def wave(self):
|
147 |
self.__client.send_app_message(
|
148 |
{
|
149 |
+
"prompt": self.__params.prompt,
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
}
|
151 |
)
|
152 |
|
frontend/app/components/Call.js
CHANGED
@@ -1,17 +1,18 @@
|
|
1 |
"use client";
|
2 |
|
3 |
-
import { useCallback, useEffect, useState } from "react";
|
4 |
-
import Card from "../components/Card";
|
5 |
-
import { VideoCameraIcon, PaintBrushIcon } from "@heroicons/react/24/outline";
|
6 |
-
import Avatar from "../components/Avatar";
|
7 |
-
import CreateRoom from "../components/CreateRoom";
|
8 |
-
import { apiUrl } from "../utils";
|
9 |
-
import Join from "../components/Joining";
|
10 |
import {
|
11 |
DailyVideo,
|
|
|
12 |
useLocalSessionId,
|
13 |
useParticipantIds,
|
14 |
} from "@daily-co/daily-react";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
|
16 |
const STATE_IDLE = "idle";
|
17 |
const STATE_JOINING = "joining";
|
@@ -24,8 +25,13 @@ export default function Call() {
|
|
24 |
const [callState, setCallState] = useState(STATE_IDLE);
|
25 |
const [roomUrl, setRoomUrl] = useState();
|
26 |
const [botState, setBotState] = useState(BOT_STATE_STARTING);
|
|
|
|
|
27 |
const localSessionId = useLocalSessionId();
|
28 |
const participantIds = useParticipantIds({ filter: "remote" });
|
|
|
|
|
|
|
29 |
|
30 |
const start = useCallback(async () => {
|
31 |
const resp = await fetch(`${apiUrl}/start`, {
|
@@ -62,11 +68,11 @@ export default function Call() {
|
|
62 |
return <Join roomUrl={roomUrl} onJoin={() => setCallState(STATE_JOINED)} />;
|
63 |
}
|
64 |
|
|
|
65 |
// Main call loop
|
66 |
return (
|
67 |
-
<main className="container py-
|
68 |
-
|
69 |
-
<div className="grid grid-cols-2 grid-flow-col gap-4">
|
70 |
<div>
|
71 |
<Card headerText="Local Webcam" HeaderIcon={VideoCameraIcon}>
|
72 |
<div className="overflow-hidden bg-gray-50 sm:rounded-lg">
|
@@ -75,7 +81,31 @@ export default function Call() {
|
|
75 |
</div>
|
76 |
</div>
|
77 |
</Card>
|
78 |
-
<div className="relative">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
</div>
|
80 |
<div>
|
81 |
<Card headerText="Inference" HeaderIcon={PaintBrushIcon}>
|
|
|
1 |
"use client";
|
2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
import {
|
4 |
DailyVideo,
|
5 |
+
useAppMessage,
|
6 |
useLocalSessionId,
|
7 |
useParticipantIds,
|
8 |
} from "@daily-co/daily-react";
|
9 |
+
import { PaintBrushIcon, VideoCameraIcon } from "@heroicons/react/24/outline";
|
10 |
+
import { useCallback, useEffect, useState } from "react";
|
11 |
+
import Avatar from "../components/Avatar";
|
12 |
+
import Card from "../components/Card";
|
13 |
+
import CreateRoom from "../components/CreateRoom";
|
14 |
+
import Join from "../components/Joining";
|
15 |
+
import { apiUrl } from "../utils";
|
16 |
|
17 |
const STATE_IDLE = "idle";
|
18 |
const STATE_JOINING = "joining";
|
|
|
25 |
const [callState, setCallState] = useState(STATE_IDLE);
|
26 |
const [roomUrl, setRoomUrl] = useState();
|
27 |
const [botState, setBotState] = useState(BOT_STATE_STARTING);
|
28 |
+
const [params, setParams] = useState({});
|
29 |
+
|
30 |
const localSessionId = useLocalSessionId();
|
31 |
const participantIds = useParticipantIds({ filter: "remote" });
|
32 |
+
const sendAppMessage = useAppMessage({
|
33 |
+
onAppMessage: useCallback((ev) => setParams(ev.data), []),
|
34 |
+
});
|
35 |
|
36 |
const start = useCallback(async () => {
|
37 |
const resp = await fetch(`${apiUrl}/start`, {
|
|
|
68 |
return <Join roomUrl={roomUrl} onJoin={() => setCallState(STATE_JOINED)} />;
|
69 |
}
|
70 |
|
71 |
+
console.log(params);
|
72 |
// Main call loop
|
73 |
return (
|
74 |
+
<main className="container py-12">
|
75 |
+
<div className="grid grid-cols-2 grid-flow-col gap-12">
|
|
|
76 |
<div>
|
77 |
<Card headerText="Local Webcam" HeaderIcon={VideoCameraIcon}>
|
78 |
<div className="overflow-hidden bg-gray-50 sm:rounded-lg">
|
|
|
81 |
</div>
|
82 |
</div>
|
83 |
</Card>
|
84 |
+
<div className="relative">
|
85 |
+
<div>
|
86 |
+
<label
|
87 |
+
htmlFor="comment"
|
88 |
+
className="block text-sm font-medium leading-6 text-gray-900"
|
89 |
+
>
|
90 |
+
Add your comment
|
91 |
+
</label>
|
92 |
+
<div className="mt-2">
|
93 |
+
<textarea
|
94 |
+
rows={4}
|
95 |
+
name="comment"
|
96 |
+
id="comment"
|
97 |
+
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
|
98 |
+
defaultValue={params.prompt || ""}
|
99 |
+
/>
|
100 |
+
</div>
|
101 |
+
</div>
|
102 |
+
Config - Resolution, Mbps, FPS
|
103 |
+
<button
|
104 |
+
onClick={() => sendAppMessage({ prompt: "Big ol' car" }, "*")}
|
105 |
+
>
|
106 |
+
Send test app message
|
107 |
+
</button>
|
108 |
+
</div>
|
109 |
</div>
|
110 |
<div>
|
111 |
<Card headerText="Inference" HeaderIcon={PaintBrushIcon}>
|
frontend/app/components/CreateRoom.js
CHANGED
@@ -1,9 +1,11 @@
|
|
1 |
-
import { ArrowRightIcon } from "@heroicons/react/20/solid";
|
2 |
import { useState } from "react";
|
3 |
import { apiUrl } from "../utils";
|
4 |
|
5 |
export default function CreateRoom({ onCreateRoom }) {
|
6 |
const [fetching, setFetching] = useState(false);
|
|
|
|
|
7 |
|
8 |
async function create() {
|
9 |
setFetching(true);
|
@@ -48,6 +50,45 @@ export default function CreateRoom({ onCreateRoom }) {
|
|
48 |
<ArrowRightIcon className="-h-5 w-5" aria-hidden="true" />
|
49 |
</button>
|
50 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
</div>
|
52 |
</div>
|
53 |
);
|
|
|
1 |
+
import { ArrowRightIcon, ChevronRightIcon } from "@heroicons/react/20/solid";
|
2 |
import { useState } from "react";
|
3 |
import { apiUrl } from "../utils";
|
4 |
|
5 |
export default function CreateRoom({ onCreateRoom }) {
|
6 |
const [fetching, setFetching] = useState(false);
|
7 |
+
const [roomUrl, setRoomUrl] = useState();
|
8 |
+
const [showManual, setShowManual] = useState(false);
|
9 |
|
10 |
async function create() {
|
11 |
setFetching(true);
|
|
|
50 |
<ArrowRightIcon className="-h-5 w-5" aria-hidden="true" />
|
51 |
</button>
|
52 |
</div>
|
53 |
+
|
54 |
+
<hr className="my-6" />
|
55 |
+
|
56 |
+
<div
|
57 |
+
className="inline-flex text-sm text-gray-500 cursor-pointer"
|
58 |
+
onClick={() => setShowManual(!showManual)}
|
59 |
+
>
|
60 |
+
Join an existing room
|
61 |
+
<ChevronRightIcon
|
62 |
+
className={`w-5 h-5 text-gray-300 transition-transform ${
|
63 |
+
showManual && "rotate-90"
|
64 |
+
}`}
|
65 |
+
/>
|
66 |
+
</div>
|
67 |
+
|
68 |
+
{showManual && (
|
69 |
+
<div className="mt-5 sm:flex sm:items-center">
|
70 |
+
<div className="w-full">
|
71 |
+
<label htmlFor="room" className="sr-only">
|
72 |
+
Daily Room URL
|
73 |
+
</label>
|
74 |
+
<input
|
75 |
+
type="text"
|
76 |
+
name="room"
|
77 |
+
id="room"
|
78 |
+
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
|
79 |
+
placeholder="https://..."
|
80 |
+
onChange={(e) => setRoomUrl(e.target.value)}
|
81 |
+
/>
|
82 |
+
</div>
|
83 |
+
<button
|
84 |
+
type="submit"
|
85 |
+
onClick={() => onCreateRoom(roomUrl)}
|
86 |
+
className="mt-3 inline-flex w-full items-center justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 sm:ml-3 sm:mt-0 sm:w-auto"
|
87 |
+
>
|
88 |
+
Join
|
89 |
+
</button>
|
90 |
+
</div>
|
91 |
+
)}
|
92 |
</div>
|
93 |
</div>
|
94 |
);
|
frontend/package.json
CHANGED
@@ -14,11 +14,13 @@
|
|
14 |
"@headlessui/react": "^1.7.17",
|
15 |
"@heroicons/react": "^2.0.18",
|
16 |
"next": "14.0.3",
|
|
|
17 |
"react": "^18",
|
18 |
"react-dom": "^18",
|
19 |
"recoil": "^0.7.7"
|
20 |
},
|
21 |
"devDependencies": {
|
|
|
22 |
"autoprefixer": "^10.0.1",
|
23 |
"eslint": "^8",
|
24 |
"eslint-config-next": "14.0.3",
|
|
|
14 |
"@headlessui/react": "^1.7.17",
|
15 |
"@heroicons/react": "^2.0.18",
|
16 |
"next": "14.0.3",
|
17 |
+
"nextjs": "^0.0.3",
|
18 |
"react": "^18",
|
19 |
"react-dom": "^18",
|
20 |
"recoil": "^0.7.7"
|
21 |
},
|
22 |
"devDependencies": {
|
23 |
+
"@tailwindcss/forms": "^0.5.7",
|
24 |
"autoprefixer": "^10.0.1",
|
25 |
"eslint": "^8",
|
26 |
"eslint-config-next": "14.0.3",
|
frontend/tailwind.config.js
CHANGED
@@ -24,5 +24,5 @@ module.exports = {
|
|
24 |
},
|
25 |
},
|
26 |
},
|
27 |
-
plugins: [],
|
28 |
};
|
|
|
24 |
},
|
25 |
},
|
26 |
},
|
27 |
+
plugins: [require("@tailwindcss/forms")],
|
28 |
};
|
frontend/yarn.lock
CHANGED
@@ -285,6 +285,13 @@
|
|
285 |
dependencies:
|
286 |
tslib "^2.4.0"
|
287 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
288 |
"@types/json5@^0.0.29":
|
289 |
version "0.0.29"
|
290 |
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
@@ -1721,6 +1728,11 @@ micromatch@^4.0.4, micromatch@^4.0.5:
|
|
1721 |
braces "^3.0.2"
|
1722 |
picomatch "^2.3.1"
|
1723 |
|
|
|
|
|
|
|
|
|
|
|
1724 |
minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
1725 |
version "3.1.2"
|
1726 |
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
@@ -1785,6 +1797,11 @@ [email protected]:
|
|
1785 |
"@next/swc-win32-ia32-msvc" "14.0.3"
|
1786 |
"@next/swc-win32-x64-msvc" "14.0.3"
|
1787 |
|
|
|
|
|
|
|
|
|
|
|
1788 |
node-releases@^2.0.14:
|
1789 |
version "2.0.14"
|
1790 |
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b"
|
|
|
285 |
dependencies:
|
286 |
tslib "^2.4.0"
|
287 |
|
288 |
+
"@tailwindcss/forms@^0.5.7":
|
289 |
+
version "0.5.7"
|
290 |
+
resolved "https://registry.yarnpkg.com/@tailwindcss/forms/-/forms-0.5.7.tgz#db5421f062a757b5f828bc9286ba626c6685e821"
|
291 |
+
integrity sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==
|
292 |
+
dependencies:
|
293 |
+
mini-svg-data-uri "^1.2.3"
|
294 |
+
|
295 |
"@types/json5@^0.0.29":
|
296 |
version "0.0.29"
|
297 |
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
|
|
1728 |
braces "^3.0.2"
|
1729 |
picomatch "^2.3.1"
|
1730 |
|
1731 |
+
mini-svg-data-uri@^1.2.3:
|
1732 |
+
version "1.4.4"
|
1733 |
+
resolved "https://registry.yarnpkg.com/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz#8ab0aabcdf8c29ad5693ca595af19dd2ead09939"
|
1734 |
+
integrity sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==
|
1735 |
+
|
1736 |
minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
1737 |
version "3.1.2"
|
1738 |
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
|
|
1797 |
"@next/swc-win32-ia32-msvc" "14.0.3"
|
1798 |
"@next/swc-win32-x64-msvc" "14.0.3"
|
1799 |
|
1800 |
+
nextjs@^0.0.3:
|
1801 |
+
version "0.0.3"
|
1802 |
+
resolved "https://registry.yarnpkg.com/nextjs/-/nextjs-0.0.3.tgz#4f4d1d6a257be920d9b9649d4d9522c724a4e543"
|
1803 |
+
integrity sha512-mYbDUo4/sRAZ8TqK63PCpYnFiLg7BICG/ot9+guOrUKd4/Fo71ZmEQ41IZbH6nqbQvG7SXTBuofJXAIWfNho0w==
|
1804 |
+
|
1805 |
node-releases@^2.0.14:
|
1806 |
version "2.0.14"
|
1807 |
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b"
|