Spaces:
Running
on
Zero
Running
on
Zero
livetest
Browse files- staticfiles/aivtuber.js +1 -0
- staticfiles/livetest.html +570 -0
- text.txt +1 -1
staticfiles/aivtuber.js
CHANGED
@@ -235,6 +235,7 @@ const getNextComment = () => {
|
|
235 |
}
|
236 |
|
237 |
const handleNewLiveCommentIfNeeded = async () => {
|
|
|
238 |
if (liveCommentQueues.length == 0) {
|
239 |
// QUEUEがなければ何もしない
|
240 |
setTimeout(handleNewLiveCommentIfNeeded, INTERVAL_MILL_SECONDS_HANDLING_COMMENTS);
|
|
|
235 |
}
|
236 |
|
237 |
const handleNewLiveCommentIfNeeded = async () => {
|
238 |
+
alert("get live chat");
|
239 |
if (liveCommentQueues.length == 0) {
|
240 |
// QUEUEがなければ何もしない
|
241 |
setTimeout(handleNewLiveCommentIfNeeded, INTERVAL_MILL_SECONDS_HANDLING_COMMENTS);
|
staticfiles/livetest.html
ADDED
@@ -0,0 +1,570 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html>
|
3 |
+
|
4 |
+
<head>
|
5 |
+
<meta charset="utf-8">
|
6 |
+
<meta name="viewport" content="width=device-width">
|
7 |
+
<title>Sample Chat AI VTuber</title>
|
8 |
+
<link rel="stylesheet" href="aivtuber.css">
|
9 |
+
<script src="./aivtuber.js"></script>
|
10 |
+
<title>Live2Dサンプル</title>
|
11 |
+
<!-- Live2D -->
|
12 |
+
<script src="./live2dcubismcore.js"></script>
|
13 |
+
<script src="//cdn.jsdelivr.net/gh/dylanNew/live2d/webgl/Live2D/lib/live2d.min.js"></script>
|
14 |
+
<!-- PixiJS -->
|
15 |
+
<script src="//cdnjs.cloudflare.com/ajax/libs/pixi.js/5.1.3/pixi.min.js"></script>
|
16 |
+
<script src="//cdn.jsdelivr.net/npm/pixi-live2d-display/dist/index.min.js"></script>
|
17 |
+
<!-- Kalidokit -->
|
18 |
+
<script src="//cdn.jsdelivr.net/npm/kalidokit@1.1/dist/kalidokit.umd.js"></script>
|
19 |
+
</head>
|
20 |
+
|
21 |
+
<body>
|
22 |
+
|
23 |
+
<video id="my-video" autoplay></video>
|
24 |
+
|
25 |
+
<script>
|
26 |
+
|
27 |
+
'use strict';
|
28 |
+
class MiiboAvatar {
|
29 |
+
constructor(config) {
|
30 |
+
this.container = config.container;
|
31 |
+
this.speechToTextOptions = config.option.speech_to_text;
|
32 |
+
this.miiboOptions = config.option.miibo;
|
33 |
+
this.didOptions = config.option.d_id;
|
34 |
+
this.initialize();
|
35 |
+
}
|
36 |
+
|
37 |
+
initialize() {
|
38 |
+
alert("38");
|
39 |
+
const RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
|
40 |
+
|
41 |
+
this.videoElement = document.getElementById(this.container);
|
42 |
+
this.videoElement.setAttribute('playsinline', '');
|
43 |
+
this.playIdleVideo();
|
44 |
+
|
45 |
+
this.createNewStream();
|
46 |
+
this.rec = new webkitSpeechRecognition()
|
47 |
+
|
48 |
+
this.speaching = false;
|
49 |
+
this.processing = false;
|
50 |
+
this.streams = [];
|
51 |
+
}
|
52 |
+
|
53 |
+
async createNewStream() {
|
54 |
+
alert("53")
|
55 |
+
try {
|
56 |
+
this.stopAllStreams();
|
57 |
+
this.closePC();
|
58 |
+
|
59 |
+
let presenter = {"source_url": this.didOptions.presenter.image_url}
|
60 |
+
const sessionResponse = await this.fetchWithRetries(`https://api.d-id.com/${this.didOptions.service}/streams`, {
|
61 |
+
method: 'POST',
|
62 |
+
headers: {
|
63 |
+
Authorization: `Basic ${this.didOptions.key}`,
|
64 |
+
'Content-Type': 'application/json',
|
65 |
+
},
|
66 |
+
body: JSON.stringify(presenter),
|
67 |
+
});
|
68 |
+
|
69 |
+
const { id: newStreamId, offer, ice_servers: iceServers, session_id: newSessionId } = await sessionResponse.json();
|
70 |
+
this.streamId = newStreamId;
|
71 |
+
this.sessionId = newSessionId;
|
72 |
+
|
73 |
+
try {
|
74 |
+
this.sessionClientAnswer = await this.createPeerConnection(offer, iceServers);
|
75 |
+
} catch (e) {
|
76 |
+
console.log('Error creating peer connection:', e);
|
77 |
+
this.stopAllStreams();
|
78 |
+
this.closePC();
|
79 |
+
return;
|
80 |
+
}
|
81 |
+
|
82 |
+
const sdpResponse = await fetch(`https://api.d-id.com/${this.didOptions.service}/streams/${this.streamId}/sdp`, {
|
83 |
+
method: 'POST',
|
84 |
+
headers: {
|
85 |
+
Authorization: `Basic ${this.didOptions.key}`,
|
86 |
+
'Content-Type': 'application/json',
|
87 |
+
},
|
88 |
+
body: JSON.stringify({
|
89 |
+
answer: this.sessionClientAnswer,
|
90 |
+
session_id: this.sessionId,
|
91 |
+
}),
|
92 |
+
});
|
93 |
+
|
94 |
+
// Handle sdpResponse if needed
|
95 |
+
} catch (error) {
|
96 |
+
console.log('Error creating new stream:', error);
|
97 |
+
// Handle error
|
98 |
+
}
|
99 |
+
}
|
100 |
+
|
101 |
+
speechRecogInit() {
|
102 |
+
this.audioContext = new (window.AudioContext || window.webkitAudioContext)()
|
103 |
+
|
104 |
+
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
105 |
+
navigator.mediaDevices.getUserMedia({audio: true}).then((stream) => {
|
106 |
+
var input = this.audioContext.createMediaStreamSource(stream)
|
107 |
+
this.audioContext.resume()
|
108 |
+
this.recorder = new Recorder(input)
|
109 |
+
})
|
110 |
+
}
|
111 |
+
}
|
112 |
+
|
113 |
+
|
114 |
+
startRecording() {
|
115 |
+
this.recorder && this.recorder.record();
|
116 |
+
}
|
117 |
+
|
118 |
+
stopRecording() {
|
119 |
+
this.playLoadingVideo();
|
120 |
+
this.recorder && this.recorder.stop();
|
121 |
+
this.audioRecognize();
|
122 |
+
this.recorder.clear();
|
123 |
+
}
|
124 |
+
|
125 |
+
audioRecognize() {
|
126 |
+
this.recorder && this.recorder.exportWAV((blob) => {
|
127 |
+
let reader = new FileReader()
|
128 |
+
reader.onload = () => {
|
129 |
+
let result = new Uint8Array(reader.result)
|
130 |
+
|
131 |
+
let data = {
|
132 |
+
"config": {
|
133 |
+
"encoding": "LINEAR16",
|
134 |
+
"languageCode": "ja-JP",
|
135 |
+
"alternativeLanguageCodes": ["en-US"],//,"cmn-CN","ko-KR"],
|
136 |
+
"audio_channel_count": 2
|
137 |
+
},
|
138 |
+
"audio": {
|
139 |
+
"content": this.arrayBufferToBase64(result)
|
140 |
+
}
|
141 |
+
}
|
142 |
+
fetch('https://speech.googleapis.com/v1/speech:recognize?key=' + this.speechToTextOptions.api_key, {
|
143 |
+
method: 'POST',
|
144 |
+
headers: {
|
145 |
+
'Content-Type': 'application/json; charset=utf-8'
|
146 |
+
},
|
147 |
+
body: JSON.stringify(data)
|
148 |
+
}).then((response) => {
|
149 |
+
return response.text()
|
150 |
+
}).then((text) => {
|
151 |
+
let result_json = JSON.parse(text)
|
152 |
+
text = result_json.results[0].alternatives[0].transcript;
|
153 |
+
this.languageCode = result_json.results[0].languageCode;
|
154 |
+
this.ask(text)
|
155 |
+
})
|
156 |
+
}
|
157 |
+
reader.readAsArrayBuffer(blob)
|
158 |
+
})
|
159 |
+
}
|
160 |
+
// Chrome Only
|
161 |
+
autoRecognizeMessage(message) {
|
162 |
+
handleLiveComment("test",message)
|
163 |
+
alert("start word test")
|
164 |
+
this.rec.continuous = false
|
165 |
+
this.rec.interimResults = false
|
166 |
+
this.rec.lang = 'ja-JP'
|
167 |
+
this.processing = true
|
168 |
+
this.playLoadingVideo();
|
169 |
+
|
170 |
+
this.rec.stop()
|
171 |
+
alert("startssssssssssssssssssssssssss")
|
172 |
+
const { transcript } = message
|
173 |
+
this.ask(transcript);
|
174 |
+
//for (var i = e.resultIndex; i < e.results.length; i++) {
|
175 |
+
// if (!e.results[i].isFinal) continue
|
176 |
+
//
|
177 |
+
// const { transcript } = e.results[i][0]
|
178 |
+
// this.ask(transcript);
|
179 |
+
//}
|
180 |
+
//this.rec.onresult = (e) => {
|
181 |
+
|
182 |
+
//}
|
183 |
+
|
184 |
+
this.rec.onend = () => { this.autoRecognizeRestart() }
|
185 |
+
this.rec.start()
|
186 |
+
}
|
187 |
+
// Chrome Only
|
188 |
+
autoRecognize() {
|
189 |
+
this.rec.continuous = false
|
190 |
+
this.rec.interimResults = false
|
191 |
+
this.rec.lang = 'ja-JP'
|
192 |
+
|
193 |
+
this.rec.onresult = (e) => {
|
194 |
+
this.processing = true
|
195 |
+
this.playLoadingVideo();
|
196 |
+
|
197 |
+
this.rec.stop()
|
198 |
+
alert("start")
|
199 |
+
|
200 |
+
for (var i = e.resultIndex; i < e.results.length; i++) {
|
201 |
+
if (!e.results[i].isFinal) continue
|
202 |
+
|
203 |
+
const { transcript } = e.results[i][0]
|
204 |
+
this.ask(transcript);
|
205 |
+
}
|
206 |
+
}
|
207 |
+
|
208 |
+
this.rec.onend = () => { this.autoRecognizeRestart() }
|
209 |
+
this.rec.start()
|
210 |
+
}
|
211 |
+
|
212 |
+
autoRecognizeRestart() {
|
213 |
+
if (this.processing) {
|
214 |
+
setTimeout(() => {this.autoRecognizeRestart()}, 1000)
|
215 |
+
} else {
|
216 |
+
this.rec.start()
|
217 |
+
}
|
218 |
+
}
|
219 |
+
|
220 |
+
arrayBufferToBase64(buffer) {
|
221 |
+
let binary = ''
|
222 |
+
let bytes = new Uint8Array(buffer);
|
223 |
+
let len = bytes.byteLength
|
224 |
+
for (let i = 0; i < len; i++) {
|
225 |
+
binary += String.fromCharCode(bytes[i])
|
226 |
+
}
|
227 |
+
return window.btoa(binary)
|
228 |
+
}
|
229 |
+
|
230 |
+
ask(message) {
|
231 |
+
//youtubeからの質問
|
232 |
+
alert("203 ask"+message);
|
233 |
+
//ask(start);
|
234 |
+
this.getMiiboResponse(message);
|
235 |
+
}
|
236 |
+
|
237 |
+
async getMiiboResponse(utterance) {
|
238 |
+
alert("209 getMiiboResponse"+utterance)
|
239 |
+
const params = {
|
240 |
+
api_key: this.miiboOptions.api_key,
|
241 |
+
agent_id: this.miiboOptions.agent_id,
|
242 |
+
uid: this.miiboOptions.user_id,
|
243 |
+
stream: true,
|
244 |
+
utterance: utterance
|
245 |
+
};
|
246 |
+
|
247 |
+
try {
|
248 |
+
const res = await fetch("https://api-mebo.dev/api", {
|
249 |
+
method: "POST",
|
250 |
+
headers: { "Content-Type": "application/json" },
|
251 |
+
body: JSON.stringify(params),
|
252 |
+
});
|
253 |
+
|
254 |
+
const reader = res.body.getReader();
|
255 |
+
const decoder = new TextDecoder();
|
256 |
+
let output = "";
|
257 |
+
let sentences = [];
|
258 |
+
let current_index = 0;
|
259 |
+
|
260 |
+
const read = async () => {
|
261 |
+
const { done, value } = await reader.read();
|
262 |
+
if (done) return;
|
263 |
+
|
264 |
+
let dataString = decoder.decode(value).split("\n").filter(x => x != "");
|
265 |
+
|
266 |
+
try {
|
267 |
+
let responseData = JSON.parse(dataString[dataString.length - 1]);
|
268 |
+
|
269 |
+
output = responseData.bestResponse.utterance.split("\n").filter(x => x.trim() != "").join("\n");
|
270 |
+
sentences = output.replace(/[。、\.\,\!\?!?]/,".").split(".")
|
271 |
+
if (this.didOptions.priority == "speed" && current_index == 0 && current_index + 1 < sentences.length) {
|
272 |
+
this.startTalk(sentences[current_index++])
|
273 |
+
}
|
274 |
+
} catch(e) {
|
275 |
+
console.log(e);
|
276 |
+
}
|
277 |
+
|
278 |
+
return read();
|
279 |
+
};
|
280 |
+
|
281 |
+
await read();
|
282 |
+
reader.releaseLock();
|
283 |
+
|
284 |
+
this.startTalk(sentences.slice(current_index).join("。"));
|
285 |
+
} catch(error) {
|
286 |
+
console.log("Error fetching AI response: ", error);
|
287 |
+
}
|
288 |
+
}
|
289 |
+
|
290 |
+
async startTalk(input) {
|
291 |
+
if (this.peerConnection?.signalingState === 'stable' || this.peerConnection?.iceConnectionState === 'connected') {
|
292 |
+
|
293 |
+
const gender = this.didOptions.presenter.gender;
|
294 |
+
let voice_id = this.didOptions.presenter.voice_id || "";
|
295 |
+
|
296 |
+
if (voice_id == "") {
|
297 |
+
switch (this.languageCode) {
|
298 |
+
case "en-us":
|
299 |
+
voice_id = gender == "male" ? "en-US-GuyNeural" : "en-US-AriaNeural"
|
300 |
+
break;
|
301 |
+
case "ko-kr":
|
302 |
+
voice_id = gender == "male" ? "ko-KR-InJoonNeural" : "ko-KR-YuJinNeural"
|
303 |
+
break;
|
304 |
+
case "cmn-CN":
|
305 |
+
voice_id = gender == "male" ? "zh-CN-YunjianNeural" : "zh-CN-XiaohanNeural"
|
306 |
+
break;
|
307 |
+
default:
|
308 |
+
voice_id = gender == "male" ? "ja-JP-KeitaNeural" : "ja-JP-NanamiNeural"
|
309 |
+
}
|
310 |
+
}
|
311 |
+
|
312 |
+
const requestOptions = {
|
313 |
+
method: 'POST',
|
314 |
+
headers: {
|
315 |
+
Authorization: `Basic ${this.didOptions.key}`,
|
316 |
+
'Content-Type': 'application/json',
|
317 |
+
},
|
318 |
+
body: JSON.stringify({
|
319 |
+
script: {
|
320 |
+
type: "text",
|
321 |
+
subtitles: false,
|
322 |
+
provider: {
|
323 |
+
type: "microsoft",
|
324 |
+
voice_id: voice_id
|
325 |
+
},
|
326 |
+
ssml: false,
|
327 |
+
input: input
|
328 |
+
},
|
329 |
+
config: {
|
330 |
+
fluent: false,
|
331 |
+
pad_audio: 0,
|
332 |
+
align_driver: false,
|
333 |
+
stitch: false,
|
334 |
+
auto_match: false,
|
335 |
+
sharpen: false,
|
336 |
+
normalization_factor: 0
|
337 |
+
},
|
338 |
+
session_id: this.sessionId,
|
339 |
+
}),
|
340 |
+
};
|
341 |
+
|
342 |
+
if (this.didOptions.service === 'clips') {
|
343 |
+
requestOptions.body.background = { color: '#FFFFFF' };
|
344 |
+
}
|
345 |
+
|
346 |
+
try {
|
347 |
+
const playResponse = await this.fetchWithRetries(`https://api.d-id.com/${this.didOptions.service}/streams/${this.streamId}`, requestOptions);
|
348 |
+
// Handle response if needed
|
349 |
+
} catch (error) {
|
350 |
+
console.error('Error starting talk:', error);
|
351 |
+
// Handle error
|
352 |
+
}
|
353 |
+
}
|
354 |
+
}
|
355 |
+
|
356 |
+
async destoryTalk(input) {
|
357 |
+
try {
|
358 |
+
await fetch(`https://api.d-id.com/${this.didOptions.service}/streams/${this.streamId}`, {
|
359 |
+
method: 'DELETE',
|
360 |
+
headers: {
|
361 |
+
Authorization: `Basic ${this.didOptions.key}`,
|
362 |
+
'Content-Type': 'application/json',
|
363 |
+
},
|
364 |
+
body: JSON.stringify({ session_id: this.sessionId }),
|
365 |
+
});
|
366 |
+
|
367 |
+
await this.stopAllStreams();
|
368 |
+
await this.closePC();
|
369 |
+
} catch (error) {
|
370 |
+
console.error('Error destroying talk:', error);
|
371 |
+
// Handle error
|
372 |
+
}
|
373 |
+
}
|
374 |
+
|
375 |
+
onIceCandidate(event) {
|
376 |
+
if (event.candidate) {
|
377 |
+
const { candidate, sdpMid, sdpMLineIndex } = event.candidate;
|
378 |
+
|
379 |
+
fetch(`https://api.d-id.com/${this.didOptions.service}/streams/${this.streamId}/ice`, {
|
380 |
+
method: 'POST',
|
381 |
+
headers: {
|
382 |
+
Authorization: `Basic ${this.didOptions.key}`,
|
383 |
+
'Content-Type': 'application/json',
|
384 |
+
},
|
385 |
+
body: JSON.stringify({
|
386 |
+
candidate,
|
387 |
+
sdpMid,
|
388 |
+
sdpMLineIndex,
|
389 |
+
session_id: this.sessionId,
|
390 |
+
}),
|
391 |
+
}).catch((error) => {
|
392 |
+
console.error('Error sending ICE candidate:', error);
|
393 |
+
// Handle error
|
394 |
+
});
|
395 |
+
}
|
396 |
+
}
|
397 |
+
|
398 |
+
onIceConnectionStateChange() {
|
399 |
+
if (this.peerConnection.iceConnectionState === 'failed' || this.peerConnection.iceConnectionState === 'closed') {
|
400 |
+
this.stopAllStreams();
|
401 |
+
this.closePC();
|
402 |
+
}
|
403 |
+
}
|
404 |
+
|
405 |
+
onTrack(event) {
|
406 |
+
if (!event.track || !event.streams || event.streams.length === 0) return;
|
407 |
+
|
408 |
+
this.statsIntervalId = setInterval(async () => {
|
409 |
+
const stats = await this.peerConnection.getStats(event.track);
|
410 |
+
stats.forEach((report) => {
|
411 |
+
if (report.type === 'inbound-rtp' && report.mediaType === 'video') {
|
412 |
+
const videoStatusChanged = this.videoIsPlaying !== report.bytesReceived > this.lastBytesReceived;
|
413 |
+
|
414 |
+
if (videoStatusChanged) {
|
415 |
+
this.videoIsPlaying = report.bytesReceived > this.lastBytesReceived;
|
416 |
+
this.onVideoStatusChange(this.videoIsPlaying, event.streams[0]);
|
417 |
+
}
|
418 |
+
this.lastBytesReceived = report.bytesReceived;
|
419 |
+
}
|
420 |
+
});
|
421 |
+
}, 100);
|
422 |
+
}
|
423 |
+
|
424 |
+
onVideoStatusChange(videoIsPlaying, stream) {
|
425 |
+
let status;
|
426 |
+
if (videoIsPlaying) {
|
427 |
+
status = 'streaming';
|
428 |
+
const remoteStream = stream;
|
429 |
+
this.streams.push(remoteStream);
|
430 |
+
this.checkSpeaching();
|
431 |
+
} else {
|
432 |
+
status = 'empty';
|
433 |
+
this.speaching = false;
|
434 |
+
this.processing = false;
|
435 |
+
this.playIdleVideo();
|
436 |
+
}
|
437 |
+
}
|
438 |
+
|
439 |
+
checkSpeaching() {
|
440 |
+
if (this.speaching) {
|
441 |
+
setTimeout(() => {this.checkSpeaching()}, 20)
|
442 |
+
} else {
|
443 |
+
this.setVideoElement(this.streams.shift());
|
444 |
+
}
|
445 |
+
}
|
446 |
+
|
447 |
+
async createPeerConnection(offer, iceServers) {
|
448 |
+
if (!this.peerConnection) {
|
449 |
+
this.peerConnection = new RTCPeerConnection({ iceServers });
|
450 |
+
this.peerConnection.addEventListener('icecandidate', this.onIceCandidate.bind(this), true);
|
451 |
+
this.peerConnection.addEventListener('iceconnectionstatechange', this.onIceConnectionStateChange.bind(this), true);
|
452 |
+
this.peerConnection.addEventListener('track', this.onTrack.bind(this), true);
|
453 |
+
}
|
454 |
+
|
455 |
+
await this.peerConnection.setRemoteDescription(offer);
|
456 |
+
const sessionClientAnswer = await this.peerConnection.createAnswer();
|
457 |
+
await this.peerConnection.setLocalDescription(sessionClientAnswer);
|
458 |
+
return sessionClientAnswer;
|
459 |
+
}
|
460 |
+
|
461 |
+
|
462 |
+
setVideoElement(stream) {
|
463 |
+
if (!stream) return;
|
464 |
+
this.videoElement.srcObject = stream;
|
465 |
+
this.videoElement.loop = false;
|
466 |
+
|
467 |
+
// safari hotfix
|
468 |
+
if (this.videoElement.paused) {
|
469 |
+
this.videoElement
|
470 |
+
.play()
|
471 |
+
.then((_) => {})
|
472 |
+
.catch((e) => {});
|
473 |
+
}
|
474 |
+
}
|
475 |
+
|
476 |
+
playIdleVideo() {
|
477 |
+
this.videoElement.srcObject = undefined;
|
478 |
+
this.videoElement.src = this.didOptions.presenter.idle_movie;
|
479 |
+
this.videoElement.loop = true;
|
480 |
+
}
|
481 |
+
|
482 |
+
playLoadingVideo() {
|
483 |
+
this.videoElement.srcObject = undefined;
|
484 |
+
this.videoElement.src = this.didOptions.presenter.loading_movie;
|
485 |
+
this.videoElement.loop = false;
|
486 |
+
}
|
487 |
+
|
488 |
+
stopAllStreams() {
|
489 |
+
if (this.videoElement.srcObject) {
|
490 |
+
this.videoElement.srcObject.getTracks().forEach((track) => track.stop());
|
491 |
+
this.videoElement.srcObject = null;
|
492 |
+
}
|
493 |
+
}
|
494 |
+
|
495 |
+
closePC(pc = this.peerConnection) {
|
496 |
+
if (!pc) return;
|
497 |
+
pc.close();
|
498 |
+
pc.removeEventListener('icecandidate', this.onIceCandidate.bind(this), true);
|
499 |
+
pc.removeEventListener('iceconnectionstatechange', this.onIceConnectionStateChange.bind(this), true);
|
500 |
+
pc.removeEventListener('track', this.onTrack.bind(this), true);
|
501 |
+
clearInterval(this.statsIntervalId);
|
502 |
+
if (pc === this.peerConnection) {
|
503 |
+
this.peerConnection = null;
|
504 |
+
}
|
505 |
+
}
|
506 |
+
|
507 |
+
async fetchWithRetries(url, options, retries = 1) {
|
508 |
+
const maxRetryCount = 3;
|
509 |
+
const maxDelaySec = 4;
|
510 |
+
try {
|
511 |
+
return await fetch(url, options);
|
512 |
+
} catch (err) {
|
513 |
+
if (retries <= maxRetryCount) {
|
514 |
+
const delay = Math.min(Math.pow(2, retries) / 4 + Math.random(), maxDelaySec) * 1000;
|
515 |
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
516 |
+
return this.fetchWithRetries(url, options, retries + 1);
|
517 |
+
} else {
|
518 |
+
throw new Error(`Max retries exceeded. error: ${err}`);
|
519 |
+
}
|
520 |
+
}
|
521 |
+
}
|
522 |
+
}
|
523 |
+
|
524 |
+
const miiboAvatar = new MiiboAvatar({
|
525 |
+
container: "my-video",
|
526 |
+
option: {
|
527 |
+
miibo: {
|
528 |
+
api_key: "ed212f05-a0f2-4838-9bb3-26d318504a76190c9e5bc19f0",
|
529 |
+
agent_id: "3e83a6b2-3c03-42b1-abc7-8a2dd97a8999190c7d1806120c",
|
530 |
+
user_id: "user",
|
531 |
+
},
|
532 |
+
d_id: {
|
533 |
+
key: "bWl5YXRha2VuOTk4QGdtYWlsLmNvbQ:2YVtXHVy3BYRW0AHvYbPa",
|
534 |
+
service: "talks",
|
535 |
+
priority: "cost",
|
536 |
+
presenter:{
|
537 |
+
gender: "male",
|
538 |
+
image_url: "https://kenken999-fastapi-django-main-live.hf.space/static/sugi.jpg",
|
539 |
+
idle_movie: "https://github.com/miibo-takumori/resorces/raw/main/portorate-idle.mp4",
|
540 |
+
loading_movie: "https://github.com/miibo-takumori/resorces/raw/main/portorate-loading.mp4",
|
541 |
+
}
|
542 |
+
}
|
543 |
+
}
|
544 |
+
})
|
545 |
+
|
546 |
+
miiboAvatar.autoRecognize();
|
547 |
+
</script>
|
548 |
+
<canvas id="my-live2d"></canvas>
|
549 |
+
<script src="live2d.js"></script>
|
550 |
+
<script src="index.js"></script>
|
551 |
+
<div id="vtubers">
|
552 |
+
<!--
|
553 |
+
<iframe width="auto" height="315" src="https://www.youtube.com/embed/9BSgttQDyOw?si=ax2F4oAi2h9f9eGT" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
554 |
+
/-->
|
555 |
+
<!--<img id="charaImg" src="chara.png" width="auto" height="400" />/-->
|
556 |
+
</div>
|
557 |
+
<div id="aiResponse" class="aiResponseBox">
|
558 |
+
<p class="ai-response" id="aiResponseUtterance"></p>
|
559 |
+
</div>
|
560 |
+
<div class="bottomBox">
|
561 |
+
<p id="userComment"></p>
|
562 |
+
<button id="startLiveButton" onclick="startLive();">LIVE開始</button>
|
563 |
+
<div id="submit_form">
|
564 |
+
<input type="text" id="utterance" />
|
565 |
+
<button id="sendButton" onclick="onClickSend();">送信</button>
|
566 |
+
</div>
|
567 |
+
</div>
|
568 |
+
</body>
|
569 |
+
|
570 |
+
</html>
|
text.txt
CHANGED
@@ -1 +1 @@
|
|
1 |
-
|
|
|
1 |
+
いまいち買取額想像出来ないのですが結構安いってことですか?
|