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